diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000..7468fa0c5 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,17 @@ +version: "{branch} {build}" + +branches: + only: + - master + +cache: + - C:\Users\appveyor\.m2 + +install: + - SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0 + +build_script: + - mvn clean test-compile + +test_script: + - mvn verify diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..91354c172 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: maven + directory: "/" + schedule: + interval: daily + time: "09:00" + open-pull-requests-limit: 10 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..f5877a770 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,33 @@ +name: build +on: + push: + branches: + - master + - /\d\.0\.0-RC/ + pull_request: + branches: + - master + - /\d\.0\.0-RC/ +jobs: + builds-tests-coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'adopt' + - uses: actions/cache@v4 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Run build steps and generate coverage report + run: | + mvn verify javadoc:javadoc jacoco:report -Pcoverage -B -V + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@v5 + with: + file: ./**/target/site/jacoco/jacoco.xml + name: codecov + fail_ci_if_error: false diff --git a/.sonar.settings b/.sonar.settings new file mode 100644 index 000000000..1611fb1fa --- /dev/null +++ b/.sonar.settings @@ -0,0 +1,2 @@ +# Settings for sonar scan +gdc.java_version=openjdk-17 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 45b7d3d5b..000000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: java -install: true -script: mvn verify coveralls:report -Pcoverage -jdk: - - openjdk7 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..ad99aa3bf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,5 @@ +# Contributor Code of Conduct + +This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters. + +For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..150b45ca6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,101 @@ +# GoodData Java SDK - Contribution guide + +Below are few **rules, recommendations and best practices** we try to follow when developing the _GoodData Java SDK_. + +## Paperwork + +### Committer + +* The **commit message**: + * must be written in the **imperative mood** + * must clearly **explain rationale** behind this change (describe _why_ you are doing the change rather than _what_ + you are changing) +* The **pull request**: + * with non-trivial, non-dependencies change must be * + *[associated with an issue](https://help.github.com/articles/closing-issues-via-commit-messages/)** +* Add usage examples of new high level functionality to + [documentation](https://github.com/gooddata/gooddata-java/wiki/Code-Examples). + +### Reviewer + +Ensure pull request and issues are + +* **properly labeled** (trivial/bug/enhancement/backward incompatible/...) +* marked with exactly one **milestone version** + +## Design + +### Structure + +* **Package names** for DTOs and services should be named in the same manner as REST API. +* Don't create single implementation interfaces for services. + +### DTOs + +* All DTOs which can be updated should be **mutable**. Please keep mutable only the fields which are subject of change, + the rest should be immutable. +* Create method named `String getUri()` to provide **URI string to self**. +* Introduce **constant** with URI: + * ```java + public static final String URI = "/gdc/someresource/{resource-id}"; + ``` + * If you need also constants with `UriTemplate`, do not put them into DTOs not to drag Spring dependency into model + module. +* Put _Jackson_ annotations on getters rather then on fields. +* Consider the **visibility** - use `package protected` when DTO is not intended for SDK user, but is needed + in related service. +* Test all DTOs using _[JsonUnit](https://github.com/lukas-krecan/JsonUnit)_. +* **Naming**: + * `Uri` for _URI string_ of some resource + * `Link` for structure containing at least _category_ (e.g. "self") and _URI string_ + (can contain also others like _title_, _summary_, etc.) + +### Enums + +* Use enums sparingly, because they don't work with REST API changes (eg. new value added on the backend) **never use + them when deserializing**. +* Where make sense, use overloaded methods with an enum argument as well as `String` argument. + +### Services + +* When programming client for some polling return [`FutureResult`](src/main/java/com/gooddata/FutureResult.java) + to enable user asynchronous call. +* Create custom [`GoodDataException`](src/main/java/com/gooddata/GoodDataException.java) when you feel the case + is specific enough. +* Prefer DTOs to `String` or primitive arguments. +* **Method naming**: + * `get*()` when searching form single object (throw exception when no or multiple objects are found, + never return `null`) + * `find*()` when searching for multiple objects (collection of objects, never return `null`) + * `list*()` when listing whole or paged collection of objects (return collection or collection wrapped by DTO) + * `remove*()` (i.e. `remove(Project project)`) instead od `delete*()` +* In addition to unit tests, write also **integration tests** and **acceptance tests** if possible. See "What to test + where" in "Best practices" below. +* Update [documentation](https://github.com/gooddata/gooddata-java/wiki/Code-Examples) with usage examples. + +## Best practices + +* **What to test where**: + * `*Test` = unit tests + * focus on verifying bussiness logic, corner cases, various input combinations, etc. + * avoid service tests using mocked `RestTemplate` - use integration tests with mocked API responses instead + * `*IT` = integration tests + * focus on verifying all possible outcomes of API calls + * see common ancestor [`AbstractGoodDataIT`](src/test/java/com/gooddata/AbstractGoodDataIT.java) setting + up [Jadler](https://github.com/jadler-mocking/jadler/wiki) for API mocking + * `*AT` = acceptance tests + * focus on verifying the happy path against the real backend (so we're sure mocks in ITs are correct) + * see common ancestor [`AbstractGoodDataAT`](src/test/java/com/gooddata/AbstractGoodDataAT.java) setting up + GoodData endpoint based on passed environment variables +* Everything public should be **documented** using _javadoc_. +* When you need some **utility code**, look for handy utilities in used libraries first (e.g. _Spring_ has + its `StreamUtils`, `FileCopyUtils`, ...). When you decide to create new utility class, + use _abstract utility class pattern_. + +## Release candidates + +We're using branches like "3.0.0-RC" for development of new major releases containing backward incompatible changes. +Such RC branch is forked from master branch and has to be kept in sync later, when something is committed into master so +RC can be merged to master when new major version is ready. + +Do not use **cherry-pick**s to keep branches in sync! Always use **merge** from master to RC. diff --git a/LICENSE.txt b/LICENSE.txt index 33b622ec7..4c1fba780 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,7 +2,7 @@ GoodData Java SDK BSD License -Copyright (c) 2014, GoodData Corporation +Copyright (c) 2014-2025, GoodData Corporation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -25,3 +25,4058 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +================================================================================ +This product includes a number of subcomponents with separate copyright notices and license terms below. +Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license. +================================================================================ + +================================================================================ + TABLE OF CONTENTS + +================================================================================ +PART 1: SUBCOMPONENTS + +- AOP alliance (1.0) [public-domain] +- Apache Ant Core (1.10.3) [Apache-2.0] +- Apache Ant Launcher (1.10.3) [Apache-2.0] +- Apache Commons IO (2.21.0) [Apache-2.0] +- Apache Commons Lang (3.20.0) [Apache-2.0] +- Apache Groovy (4.0.28) [Apache-2.0] +- gooddata-http-client (3.0.0-SNAPSHOT) [BSD-3-Clause] +- gooddata-rest-common (3.0.0) [BSD-3-Clause] +- Gson (2.3.1) [Apache-2.0] +- Guava: Google Core Libraries for Java (17.0) [Apache-2.0, CC0-1.0] +- Hamcrest (2.2) [BSD-3-Clause] +- Hamcrest Core (2.2) [BSD-3-Clause] +- Jackson-annotations (2.13.0) [Apache-2.0] +- Jackson-core (2.13.0) [Apache-2.0] +- jackson-databind (2.13.4) [Apache-2.0] +- jadler-all (1.3.1) [MIT, Apache-2.0] +- jadler-core (1.3.1) [MIT, Apache-2.0] +- jakarta.inject (1) [Apache-2.0] +- jcommander (1.78) [Apache-2.0] +- JSON in Java (20090211) [JSON] +- json-unit (2.36.0) [Apache-2.0] +- json-unit-core (2.36.0) [Apache-2.0] +- JSONassert (1.2.3) [Apache-2.0] +- JUnit (4.12) [CPL-1.0] +- Mockito (5.14.2) [MIT, Apache-2.0] +- Objenesis (3.2) [MIT] +- Sardine WebDAV client (5.13) [Apache-2.0] +- Shazamcrest (0.11) [Apache-2.0] +- SLF4J API Module (2.0.17) [MIT] +- SLF4J Simple Binding (2.0.17) [MIT] +- SnakeYAML (1.21) [Apache-2.0, Multi-license: LGPL-2.1-or-later OR GPL-2.0-or-later OR EPL-1.0 OR BSD-3-Clause OR Apache-2.0] +- Spock Framework - Core Module (2.4-M1-groovy-4.0) [Apache-2.0] +- Spring Beans (6.0.15) [Apache-2.0] +- Spring Context (6.0.15) [Apache-2.0] +- Spring Core (6.0.15) [BSD-3-Clause, Apache-2.0] +- Spring Retry (2.0.12) [Apache-2.0] +- Spring Web (6.0.15) [Apache-2.0] +- testng (7.11.0) [Apache-2.0, AML] + +PART 2: LICENSES APPENDIX +Apache 2.0 +Creative commons Zero v1.0 Universal + +[END TABLE OF CONTENTS] +================================================================================ +PART 1: SUBCOMPONENTS +================================================================================ + +-------------------------------------------------------------------------------- +AOP alliance (1.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +public-domain + + +-------------------------------------------------------------------------------- +Apache Ant Core (1.10.3) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +/* + * 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. + */ + +W3C� SOFTWARE NOTICE AND LICENSE +http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + +This work (and included software, documentation such as READMEs, or other +related items) is being provided by the copyright holders under the following +license. By obtaining, using and/or copying this work, you (the licensee) agree +that you have read, understood, and will comply with the following terms and +conditions. + +Permission to copy, modify, and distribute this software and its documentation, +with or without modification, for any purpose and without fee or royalty is +hereby granted, provided that you include the following on ALL copies of the +software and documentation or portions thereof, including modifications: + + 1. The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + 2. Any pre-existing intellectual property disclaimers, notices, or terms + and conditions. If none exist, the W3C Software Short Notice should be + included (hypertext is preferred, text is permitted) within the body + of any redistributed or derivative code. + 3. Notice of any changes or modifications to the files, including the date + changes were made. (We recommend you provide URIs to the location from + which the code is derived.) + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE +NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT +THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY +PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in advertising or +publicity pertaining to the software without specific, written prior permission. +Title to copyright in this software and any associated documentation will at +all times remain with copyright holders. + +____________________________________ + +This formulation of W3C's notice and license became active on December 31 2002. +This version removes the copyright ownership notice such that this license can +be used with materials other than those owned by the W3C, reflects that ERCIM +is now a host of the W3C, includes references to this specific dated version of +the license, and removes the ambiguous grant of "use". Otherwise, this version +is the same as the previous version and is written so as to preserve the Free +Software Foundation's assessment of GPL compatibility and OSI's certification +under the Open Source Definition. Please see our Copyright FAQ for common +questions about using materials from our site, including specific terms and +conditions for packages like libwww, Amaya, and Jigsaw. Other questions about +this notice can be directed to site-policy@w3.org. + +Joseph Reagle + +This license came from: http://www.megginson.com/SAX/copying.html + However please note future versions of SAX may be covered + under http://saxproject.org/?selected=pd + +SAX2 is Free! + +I hereby abandon any property rights to SAX 2.0 (the Simple API for +XML), and release all of the SAX 2.0 source code, compiled code, and +documentation contained in this distribution into the Public Domain. +SAX comes with NO WARRANTY or guarantee of fitness for any +purpose. + +David Megginson, david@megginson.com +2000-05-05 + + + +-------------------------------------------------------------------------------- +Apache Ant Launcher (1.10.3) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +/* + * 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. + */ + +W3C� SOFTWARE NOTICE AND LICENSE +http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + +This work (and included software, documentation such as READMEs, or other +related items) is being provided by the copyright holders under the following +license. By obtaining, using and/or copying this work, you (the licensee) agree +that you have read, understood, and will comply with the following terms and +conditions. + +Permission to copy, modify, and distribute this software and its documentation, +with or without modification, for any purpose and without fee or royalty is +hereby granted, provided that you include the following on ALL copies of the +software and documentation or portions thereof, including modifications: + + 1. The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + 2. Any pre-existing intellectual property disclaimers, notices, or terms + and conditions. If none exist, the W3C Software Short Notice should be + included (hypertext is preferred, text is permitted) within the body + of any redistributed or derivative code. + 3. Notice of any changes or modifications to the files, including the date + changes were made. (We recommend you provide URIs to the location from + which the code is derived.) + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE +NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT +THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY +PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in advertising or +publicity pertaining to the software without specific, written prior permission. +Title to copyright in this software and any associated documentation will at +all times remain with copyright holders. + +____________________________________ + +This formulation of W3C's notice and license became active on December 31 2002. +This version removes the copyright ownership notice such that this license can +be used with materials other than those owned by the W3C, reflects that ERCIM +is now a host of the W3C, includes references to this specific dated version of +the license, and removes the ambiguous grant of "use". Otherwise, this version +is the same as the previous version and is written so as to preserve the Free +Software Foundation's assessment of GPL compatibility and OSI's certification +under the Open Source Definition. Please see our Copyright FAQ for common +questions about using materials from our site, including specific terms and +conditions for packages like libwww, Amaya, and Jigsaw. Other questions about +this notice can be directed to site-policy@w3.org. + +Joseph Reagle + +This license came from: http://www.megginson.com/SAX/copying.html + However please note future versions of SAX may be covered + under http://saxproject.org/?selected=pd + +SAX2 is Free! + +I hereby abandon any property rights to SAX 2.0 (the Simple API for +XML), and release all of the SAX 2.0 source code, compiled code, and +documentation contained in this distribution into the Public Domain. +SAX comes with NO WARRANTY or guarantee of fitness for any +purpose. + +David Megginson, david@megginson.com +2000-05-05 + + + +-------------------------------------------------------------------------------- +Apache Commons IO (2.11.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + + 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. + + + + +-------------------------------------------------------------------------------- +Apache Commons Lang (3.12.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Apache Commons Lang +Copyright 2001-2021 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + + +-------------------------------------------------------------------------------- +Apache Groovy (3.0.11) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2013 Terence Parr, Sam Harwell + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.7) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.4) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. The ASF licenses this file + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.4) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.4) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. The ASF licenses this file + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.4) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. The ASF licenses this file + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.4) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. The ASF licenses this file + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.4) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. The ASF licenses this file + +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. + + +-------------------------------------------------------------------------------- +Apache Groovy (2.5.4) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. The ASF licenses this file + +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. + + +-------------------------------------------------------------------------------- +Apache HttpClient (4.5.13) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + +Apache HttpClient +Copyright 1999-2020 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). +__________________ +* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + + +-------------------------------------------------------------------------------- +Apache HttpCore (4.4.15) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Apache HttpCore +Copyright 2005-2021 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). +_____________ + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + + +-------------------------------------------------------------------------------- +Byte Buddy (without dependencies) (1.12.1) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +BSD-3-Clause, Apache-2.0 + + +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + + + * Copyright 2014 - Present Rafael Winterhalter + * + * 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. + + +-------------------------------------------------------------------------------- +Byte Buddy agent (1.12.1) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +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 + + + +-------------------------------------------------------------------------------- +cglib-nodep (3.3.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + 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. + + + +-------------------------------------------------------------------------------- +EqualsVerifier | release normal jar (3.7.2) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2022, EqualsVerifier | release normal jar Contributors + +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. + + +-------------------------------------------------------------------------------- +EqualsVerifier | release normal jar (3.10) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2022, EqualsVerifier | release normal jar Contributors + +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. + + +-------------------------------------------------------------------------------- +gooddata-http-client (1.0.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +BSD-3-Clause + +Copyright (c) 2007-2016, GoodData . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +gooddata-http-client (1.0.0+java7.fix1) +-------------------------------------------------------------------------------- + +* Declared Licenses * +BSD-3-Clause + +Copyright (c) 2007-2016, GoodData . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +gooddata-rest-common (2.0.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +BSD-3-Clause + +Copyright (c) 2004-2017, GoodData . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Gson (2.3.1) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2008 Google Inc. + +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. + + +-------------------------------------------------------------------------------- +Guava: Google Core Libraries for Java (17.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +Apache-2.0, CC0-1.0 + + + * Copyright (C) 2008 The Guava Authors + * + * 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. + */ + + + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + + +-------------------------------------------------------------------------------- +Hamcrest (2.2) +-------------------------------------------------------------------------------- + +* Declared Licenses * +BSD-3-Clause + +Copyright (c) 2022, Hamcrest Contributors . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Hamcrest Core (2.2) +-------------------------------------------------------------------------------- + +* Declared Licenses * +BSD-3-Clause + +Copyright (c) 2022, Hamcrest Core Contributors . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Jackson-annotations (2.13.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + + 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. + + + +-------------------------------------------------------------------------------- +Jackson-core (2.13.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + + 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. + + + +-------------------------------------------------------------------------------- +jackson-databind (2.13.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + + 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. + + + +-------------------------------------------------------------------------------- +jackson-databind (2.13.3) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + + 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. + + + +-------------------------------------------------------------------------------- +jadler-all (1.3.1) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +MIT, Apache-2.0 + + +Copyright (c) 2012 - 2016 Jadler contributors +This program is made available under the terms of the MIT License. + + + ~ Copyright (c) 2007-2011 Sonatype, Inc. All rights reserved. + ~ + ~ This program is licensed to you under the Apache License Version 2.0, + ~ and you may not use this file except in compliance with the Apache License Version 2.0. + ~ You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the Apache License Version 2.0 is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + --> + + +-------------------------------------------------------------------------------- +jadler-core (1.3.1) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +MIT, Apache-2.0 + + +Copyright (c) 2012 - 2016 Jadler contributors +This program is made available under the terms of the MIT License. + + + ~ Copyright (c) 2007-2011 Sonatype, Inc. All rights reserved. + ~ + ~ This program is licensed to you under the Apache License Version 2.0, + ~ and you may not use this file except in compliance with the Apache License Version 2.0. + ~ You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the Apache License Version 2.0 is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + + +-------------------------------------------------------------------------------- +jakarta.inject (1) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2009 The JSR-330 Expert Group + +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. + + +-------------------------------------------------------------------------------- +jcommander (1.78) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2010 the original author or authors. + +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. + + +-------------------------------------------------------------------------------- +JSON in Java (20090211) +-------------------------------------------------------------------------------- + +* Declared Licenses * +JSON + +Copyright (c) 2002 JSON.org +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The Software shall be used for Good, not Evil. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +json-unit (2.28.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2009-2019 the original author or authors. + +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. + + +-------------------------------------------------------------------------------- +json-unit (2.35.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2009-2019 the original author or authors. + +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. + + +-------------------------------------------------------------------------------- +json-unit-core (2.28.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2009-2019 the original author or authors. + +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. + + +-------------------------------------------------------------------------------- +json-unit-core (2.35.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2009-2019 the original author or authors. + +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. + + +-------------------------------------------------------------------------------- +JSONassert (1.2.3) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2007-2011 Sonatype, Inc. All rights reserved. + +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. + + +-------------------------------------------------------------------------------- +JUnit (4.12) +-------------------------------------------------------------------------------- + +* Declared Licenses * +CPL-1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. +DEFINITIONS +"Contribution" means: + a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. +"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. +"Program" means the Contributions distributed in accordance with this Agreement. +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. +GRANT OF RIGHTS + a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. +REQUIREMENTS +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the Program. + Contributors may not remove or alter any copyright notices contained within the Program. + Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. +COMMERCIAL DISTRIBUTION +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. +NO WARRANTY +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED 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. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. +DISCLAIMER OF LIABILITY +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. +GENERAL +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. +If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + +-------------------------------------------------------------------------------- +Mockito (4.6.1) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +MIT, Apache-2.0 + + +The MIT License + +Copyright (c) 2007 Mockito contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +Copyright 2022, Mockito Contributors + +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. + + +-------------------------------------------------------------------------------- +Mockito (4.1.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +MIT, Apache-2.0 + + +The MIT License + +Copyright (c) 2007 Mockito contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +Copyright 2022, Mockito Contributors + +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. + + +-------------------------------------------------------------------------------- +Objenesis (3.2) +-------------------------------------------------------------------------------- + +* Declared Licenses * +MIT + +Copyright (c) 2022, Objenesis Contributors +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Sardine WebDAV client (5.10) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2009-2011 Jon Stevens et al. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + +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. + + +-------------------------------------------------------------------------------- +Shazamcrest (0.11) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2011 Google Inc. + +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. + + +-------------------------------------------------------------------------------- +SLF4J API Module (2.0.0-alpha7) +-------------------------------------------------------------------------------- + +* Declared Licenses * +MIT + +Copyright (c) 2004-2017 QOS.ch + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +SLF4J Simple Binding (2.0.0-alpha7) +-------------------------------------------------------------------------------- + +* Declared Licenses * +MIT + +Copyright (c) 2004-2022 QOS.ch Sarl (Switzerland) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +SnakeYAML (1.21) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +Apache-2.0, Multi-license: LGPL-2.1-or-later OR GPL-2.0-or-later OR EPL-1.0 OR BSD-3-Clause OR Apache-2.0 + + + * Copyright (c) 2008, http://www.snakeyaml.org + * + * 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. + + +// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland +// www.source-code.biz, www.inventec.ch/chdh +// +// This module is multi-licensed and may be used under the terms +// of any of the following licenses: +// +// EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal +// LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html +// GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html +// AL, Apache License, V2.0 or later, http://www.apache.org/licenses +// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php +// +// Please contact the author if you need another license. +// This module is provided "as is", without warranties of any kind. + +package org.yaml.snakeyaml.external.biz.base64Coder; + +/** + * A Base64 encoder/decoder. + * + *

+ * This class is used to encode and decode data in Base64 format as described in + * RFC 1521. + * + *

+ * Project home page: www. + * source-code.biz/base64coder/java
+ * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
+ * Multi-licensed: EPL / LGPL / GPL / AL / BSD. + + +-------------------------------------------------------------------------------- +Spock Framework - Core Module (2.2-M1-groovy-4.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2012 the original author or authors. + +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. + + +-------------------------------------------------------------------------------- +Spock Framework - Core Module (1.3-groovy-2.5) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2012 the original author or authors. + +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 Beans (5.3.20) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2002-2021 the original author or authors. + +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 Beans (5.3.13) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2002-2021 the original author or authors. + +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 Commons Logging Bridge (5.3.13) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright ownership. + +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 Context (5.3.20) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2002-2019 the original author or authors. + +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 Core (5.3.20) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +BSD-3-Clause, Apache-2.0 + + +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + + + * Copyright 2002-2018 the original author or authors. + * + * 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 + * + * https://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 Core (5.3.13) +-------------------------------------------------------------------------------- + +* Declared Licenses * + + +* Other Licenses * +BSD-3-Clause, Apache-2.0 + + +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + + + * Copyright 2002-2018 the original author or authors. + * + * 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 + * + * https://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 Retry (1.3.1) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2006-2007 the original author or authors. + +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 Web (5.3.20) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2002-2021 the original author or authors. + +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 Web (5.3.13) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2002-2021 the original author or authors. + +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. + + +-------------------------------------------------------------------------------- +testng (7.3.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + +Copyright 2022, testng Contributors + +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. + + +* Other Licenses * +AML + + +Copyright: Copyright (c) 2006 by Apple Computer, Inc., All Rights Reserved. +IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. +In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. +The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +testng (7.6.0) +-------------------------------------------------------------------------------- + +* Declared Licenses * +Apache-2.0 + + Apache License + Version 2.0, January 2004 + https://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 + + https://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. + + +================================================================================ +PART 2: LICENSES APPENDIX +================================================================================ + + Apache License + Version 2.0, January 2004 + https://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 + + https://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. +================================================================================ + +Creative Commons Legal Code + +CC0 1.0 Universal + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); + iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + 4. Limitations and Disclaimers. + a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +Report Generated by FOSSA on 2022-6-9 diff --git a/README.md b/README.md index 1c4e27c63..3153b328c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,64 @@ # GoodData Java SDK -[![Build Status](https://travis-ci.org/martiner/gooddata-java.png?branch=master)](https://travis-ci.org/martiner/gooddata-java) +[![Build Status](https://github.com/gooddata/gooddata-java/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/gooddata/gooddata-java/actions/workflows/build.yml) +[![Coverage Status](https://codecov.io/gh/gooddata/gooddata-java/branch/master/graph/badge.svg)](https://app.codecov.io/gh/gooddata/gooddata-java/branch/master) +[![Stability: Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) +[![License](https://img.shields.io/badge/license-BSD-blue.svg)](LICENSE.txt) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fgooddata%2Fgooddata-java.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fgooddata%2Fgooddata-java?ref=badge_shield) -The *GoodData Java SDK* encapsulates the REST API provided by the [GoodData](http://www.gooddata.com) platform. -The first version was implemented during the [All Data Hackathon](http://hackathon.gooddata.com) April 10 - 11 2014 -and currently the SDK is transitioned to be an official GoodData project. +[![Javadocs](https://javadoc.io/badge/com.gooddata/gooddata-java.svg)](https://javadoc.io/doc/com.gooddata/gooddata-java) +[![Javadocs Model](https://javadoc.io/badge2/com.gooddata/gooddata-java-model/javadoc--model.svg)](https://javadoc.io/doc/com.gooddata/gooddata-java-model) +[![Maven Central](https://img.shields.io/maven-central/v/com.gooddata/gooddata-java)](https://central.sonatype.com/artifact/com.gooddata/gooddata-java) +[![Release](https://img.shields.io/github/v/release/gooddata/gooddata-java?sort=semver)](https://github.com/gooddata/gooddata-java/releases) + +The *GoodData Java SDK* encapsulates the REST API of the **GoodData Platform**. + +**The project is currently NOT in "active development". Meaning that feature request may or may not be implemented. +You are welcomed to [contribute your code](CONTRIBUTING.md) and create +an [issue](https://github.com/gooddata/gooddata-java/issues).** + +The first version was implemented during the [All Data Hackathon](http://hackathon.gooddata.com) April 10 - 11 2014. +It is free and open-source software provided "as-is" under the [BSD License](LICENSE.txt) as an official project +by [GoodData Corporation](http://www.gooddata.com). + +## Supported versions + +In order to make the user experience with integrating GoodData Java SDK as smooth and secure as possible and to ensure +that the SDK is using the latest features of the platform, we only provide support to the most recent major version of +Java SDK. + +The most recent major will be supported in the following mode: + +- The latest major version will receive new functionality and bug fixes. These changes will be applied on top of last + released version. +- GoodData customer support will provide support for the latest major version only. + +- The customers are encouraged to always use the latest version of the Java SDK. +- In case of using older versions, the user might face API incompatibility, performance or security issues. + +Please follow the [upgrade instructions](https://github.com/gooddata/gooddata-java/wiki/Upgrading) to update to the +newest version. + +### Version 5.0 Release Notes + +**Version 5.0.0** represents a major modernization release with the following key updates: + +- **Java 17**: Minimum runtime requirement upgraded from Java 11 +- **Spring 6**: Framework upgraded from Spring 5.x to Spring 6.0.15 +- **Apache HTTP Client 5**: Primary HTTP client upgraded from version 4.5.x to 5.5.1 +- **Enhanced Compatibility**: Improved support for modern Java environments and cloud platforms + +This release maintains API compatibility while modernizing the underlying infrastructure for better performance, +security, and future extensibility. + +## Modules + +The *GoodData Java SDK* contains following modules: + +* [gooddata-java](./gooddata-java) - The GoodData API client (depends on `gooddata-java-model`). +* [gooddata-java-model](./gooddata-java-model) - Lightweight library containing only GoodData API structures. +* [gooddata-java-parent](./pom.xml) - Parent for *GoodData Java SDK* libraries (just a wrapper around `gooddata-java` + and `gooddata-java-model`). ## Usage @@ -12,119 +66,183 @@ The *GoodData Java SDK* is available in Maven Central Repository, to use it from ```xml - cz.geek + com.gooddata gooddata-java - 0.10.0 + {MAJOR}.{MINOR}.{PATCH}+api{API} ``` -See [releases page](https://github.com/martiner/gooddata-java/releases) for information about versions and notable changes. +See [releases page](https://github.com/gooddata/gooddata-java/releases) for information about versions and notable +changes, +the [Upgrading Guide](https://github.com/gooddata/gooddata-java/wiki/Upgrading) will navigate you +through changes between major versions. + +See [Javadocs](http://javadoc.io/doc/com.gooddata/gooddata-java) +or [Wiki](https://github.com/gooddata/gooddata-java/wiki) for +[Code Examples](https://github.com/gooddata/gooddata-java/wiki/Code-Examples) +and [Extensibility How-To](https://github.com/gooddata/gooddata-java/wiki/Extending). + +### API version + +Since *GoodData Java SDK* version *2.32.0* API versioning is supported. The API version, GoodData Java is compatible +with, is marked in artifact version using `+api` suffix (i.e. `2.32.0+api1` is compatible with API version `1`). ### Dependencies The *GoodData Java SDK* uses: -* the [GoodData HTTP client](https://github.com/gooddata/gooddata-http-client) (version 0.8.2 or later) -* the *Apache HTTP Client* (version 4.3 or later, for white-labeled domains at least version 4.3.2 is required) -* the *Spring Framework* (version 3.x) -* the *Jackson JSON Processor* (version 1.9) -### General +* the [GoodData HTTP client](https://github.com/gooddata/gooddata-http-client) version 3.0.0 +* the *Apache HTTP Client* version 5.5.1 +* the *Apache HTTP Client* version 4.5.13 (for compatibility with Sardine WebDAV library) +* the *Spring Framework* version 6.0.15 +* the *Jackson JSON Processor* version 2.13.4 +* the *Slf4j API* version 2.0.17 +* the *Java Development Kit (JDK)* version 17 or later to build -```java -GoodData gd = new GoodData("roman@gooddata.com", "Roman1"); -gd.logout(); -``` +### Migration from version 4.x to 5.0 -### Project API +**Version 5.0 introduces several significant upgrades that may require code changes in your application:** -List projects, create a project,... -```java -ProjectService projectService = gd.getProjectService(); -Collection projects = projectService.getProjects(); -Project project = projectService.createProject(new Project("my project", "MyToken")).get(); -``` +#### Breaking Changes + +1. **Java Runtime Requirement**: Minimum Java version is now **Java 17** (previously Java 11) +2. **Spring Framework**: Upgraded from Spring 5.x to **Spring 6.0.15** +3. **Apache HTTP Client**: Primary HTTP client upgraded from version 4.5.x to **version 5.5.1** +4. **SLF4J**: Upgraded from version 1.7.x to **version 2.0.17** + +#### Migration Steps + +**1. Update Java Runtime** +- Ensure your application runs on Java 17 or later +- Update your build tools (Maven/Gradle) to use Java 17 -### Project Model API +**2. Spring Framework Compatibility** +- If your application uses Spring Framework, upgrade to Spring 6.x or later +- Review Spring 6 migration guide for additional breaking changes: https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x +- Update Spring Boot to version 3.0+ if applicable -Create and update the project model, execute MAQL DDL,... +**3. Dependency Updates** +Update your `pom.xml` or `build.gradle`: -```java -ModelService modelService = gd.getModelService(); -ModelDiff diff = modelService.getProjectModelDiff(project, - new InputStreamReader(getClass().getResourceAsStream("/person.json"))).get(); -modelService.updateProjectModel(project, diff).get(); +```xml + + + com.gooddata + gooddata-java + 5.0.0+api3 + + + + + org.springframework + spring-core + 6.0.15 + -modelService.updateProjectModel(project, "MAQL DDL EXPRESSION").get(); + + + org.slf4j + slf4j-api + 2.0.17 + ``` -### Metadata API +**4. HTTP Client Configuration** +- The SDK now internally uses Apache HTTP Client 5.x +- If you customize HTTP client settings via `GoodDataSettings`, the API remains compatible +- Custom HTTP interceptors or low-level HTTP client configurations may need updates -Query, create and update project metadata - attributes, facts, metrics, reports,... +**5. Testing** +- Thoroughly test your application after upgrade +- Pay special attention to HTTP client behavior, especially with authentication and connection pooling +- Verify logging functionality works as expected with SLF4J 2.x -```java -MetadataService md = gd.getMetadataService(); +#### Compatibility Notes -String fact = md.getObjUri(project, Fact.class, identifier("fact.person.shoesize")); -Metric m = md.createObj(project, new Metric("Avg shoe size", "SELECT AVG([" + fact + "])", "#,##0")); +- **API Compatibility**: The public SDK API remains largely backward compatible +- **Internal Changes**: HTTP client implementation has been modernized but SDK interfaces are unchanged +- **Jakarta EE**: Spring 6 uses Jakarta EE namespaces instead of Java EE (affects annotations if you use them directly) -Attribute attr = md.getObj(project, Attribute.class, identifier("attr.person.department")); +#### Known Migration Issues and Solutions -ReportDefinition definition = GridReportDefinitionContent.create( - "Department avg shoe size", - asList("metricGroup"), - asList(new AttributeInGrid(attr.getDefaultDisplayForm().getUri())), - asList(new GridElement(m.getUri(), "Avg shoe size")) -); -definition = md.createObj(project, definition); -Report report = md.createObj(project, new Report(definition.getTitle(), definition)); -``` +**1. ClassNotFoundException for Apache HTTP Client classes** +If you see errors like `ClassNotFoundException: org.apache.http.impl.client.HttpClientBuilder`: +- Remove any direct dependencies on Apache HTTP Client 4.x from your project +- The SDK now uses HTTP Client 5.x internally, which has different package names + +**2. Spring Boot Applications** +- Ensure you're using Spring Boot 3.0+ which includes Spring 6 +- Update your `@SpringBootApplication` and other Spring annotations if needed +- Check Spring Boot 3.0 migration guide for additional changes -### Dataset API +**3. Logging Configuration** +- SLF4J 2.x has some configuration changes compared to 1.7.x +- Update your `logback.xml` or `log4j2.xml` if you use advanced logging features +- Basic logging configuration should work without changes -Upload data to datasets,.. +**4. Build Tools** +```xml + + + 17 + 17 + +``` -```java -DatasetService datasetService = gd.getDatasetService(); -datasetService.loadDataset(project, "datasetId", new FileInputStream("data.csv")).get(); +```gradle +// Gradle: Ensure Java 17 in your build.gradle +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} ``` -### Report API +For additional help, please refer to the [upgrading wiki page](https://github.com/gooddata/gooddata-java/wiki/Upgrading) +or [create an issue](https://github.com/gooddata/gooddata-java/issues) if you encounter problems during migration. + +##### Retry of failed API calls -Execute and export reports. +You can retry your failed requests since *GoodData Java SDK* version *2.34.0*. Turn it on by configuring +[RetrySettings](https://github.com/gooddata/gooddata-java/blob/master/src/main/java/com/gooddata/retry/RetrySettings.java) +and add [Spring retry](https://github.com/spring-projects/spring-retry) to your classpath: -```java -ReportService reportService = gd.getReportService(); -reportService.exportReport(definition, PNG, new FileOutputStream("report.png")).get(); +```xml + + org.springframework.retry + spring-retry + 2.0.12 + ``` -### DataStore API +**Note**: Spring Retry 2.0.12 is compatible with Spring 6. If you're upgrading from prior versions of the SDK, +ensure you also upgrade your Spring Retry dependency to version 2.x for compatibility. -Manage files on the data store (currently backed by WebDAV) - user staging area. +### Logging -```java -DataStoreService dataStoreService = gd.getDataStoreService(); -dataStoreService.upload("/dir/file.txt", new FileInputStream("file.txt")); -InputStream stream = dataStoreService.download("/dir/file.txt"); -dataStoreService.delete("/dir/file.txt"); +The *GoodData Java SDK* logs using `slf4j-api`. Please adjust your logging configuration for +`com.gooddata.sdk.*` loggers or alternatively turn the logging off using following dependency: + +```xml + + org.slf4j + slf4j-nop + ``` -### Warehouse API -Manage warehouses - create, update, list and delete. -```java -WarehouseService warehouseService = gd.getWarehouseService(); -Warehouse warehouse = warehouseService.createWarehouse(new Warehouse("title", "authToken", "description")).get(); -String jdbc = warehouse.getJdbcConnectionString(); +### Date/Time -Collection warehouseList = warehouseService.listWarehouses(); -warehouseService.removeWarehouse(warehouse); -``` +The *GoodData Java SDK* is using Java 8 Date/Time API (JSR 310) for all Date / Time / Zone public facing types. +Good SO thread about differences between various types in Java Date/Time API: https://stackoverflow.com/a/32443004 -### Dataload processes API -Manage dataload processes - create, update, list, delete, and process executions - execute, get logs,... -```java -ProcessService processService = gd.getProcessService(); -DataloadProcess process = processService.createProcess(project, new DataloadProcess("name", "GRAPH"), new File("path/to/processdatadir")); +## Development -ProcessExecutionDetail executionDetail = processService.executeProcess(new ProcessExecution(process, "myGraph.grf")).get(); -processService.getExecutionLog(executionDetail, new FileOutputStream("file/where/the/log/willbewritten")); -``` +Build the library with `mvn package`, see the +[Testing](https://github.com/gooddata/gooddata-java/wiki/Testing) page for different testing methods. + +For releasing see [Releasing How-To](https://github.com/gooddata/gooddata-java/wiki/Releasing). + +## Contribute + +Found a bug? Please create an [issue](https://github.com/gooddata/gooddata-java/issues). Missing functionality? +[Contribute your code](CONTRIBUTING.md). Any questions about GoodData or this library? Check +out [the GoodData community website](http://community.gooddata.com/). diff --git a/bugs-exclude.xml b/bugs-exclude.xml new file mode 100644 index 000000000..629a72fe5 --- /dev/null +++ b/bugs-exclude.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/bugs-include.xml b/bugs-include.xml new file mode 100644 index 000000000..5926b1f8b --- /dev/null +++ b/bugs-include.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gooddata-java-model/pom.xml b/gooddata-java-model/pom.xml new file mode 100644 index 000000000..1263350d4 --- /dev/null +++ b/gooddata-java-model/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + gooddata-java-model + ${project.artifactId} + + + gooddata-java-parent + com.gooddata + 5.0.2+api3-SNAPSHOT + + + + + com.gooddata + gooddata-rest-common + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.commons + commons-lang3 + + + + org.testng + testng + test + + + org.mockito + mockito-core + test + + + org.hamcrest + hamcrest + test + + + net.javacrumbs.json-unit + json-unit + test + + + net.javacrumbs.json-unit + json-unit-core + test + + + com.shazam + shazamcrest + test + + + nl.jqno.equalsverifier + equalsverifier + test + + + commons-io + commons-io + test + + + org.spockframework + spock-core + test + + + org.apache.groovy + groovy + test + + + cglib + cglib-nodep + test + + + org.springframework + spring-web + test + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + + diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/Account.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/Account.java new file mode 100644 index 000000000..d0f0b6ae1 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/Account.java @@ -0,0 +1,234 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.account; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonView; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.util.UriHelper; + +import java.util.List; + +/** + * Account setting + */ +@JsonTypeName("accountSetting") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Account { + + public static final String URI = "/gdc/account/profile/{id}"; + public static final String ACCOUNTS_URI = "/gdc/account/domains/{organization_name}/users"; + public static final String ACCOUNT_BY_EMAIL_URI = ACCOUNTS_URI + "?login={email}"; + + public static final String LOGIN_URI = "/gdc/account/login/{id}"; + + public static final String CURRENT_ID = "current"; + + private final String login; + @JsonIgnore + private final Links links; + @JsonView(UpdateView.class) + private String email; + @JsonView(UpdateView.class) + private String password; + @JsonView(UpdateView.class) + private String verifyPassword; + @JsonView(UpdateView.class) + private String firstName; + @JsonView(UpdateView.class) + private String lastName; + @JsonView(UpdateView.class) + private List ipWhitelist; + @JsonView(UpdateView.class) + private List authenticationModes; + + @JsonCreator + private Account( + @JsonProperty("login") String login, + @JsonProperty("email") String email, + @JsonProperty("password") String password, + @JsonProperty("verifyPassword") String verifyPassword, + @JsonProperty("firstName") String firstName, + @JsonProperty("lastName") String lastName, + @JsonProperty("ipWhitelist") List ipWhitelist, + @JsonProperty("authenticationModes") List authenticationModes, + @JsonProperty("links") Links links + ) { + this.login = login; + this.email = email; + this.password = password; + this.verifyPassword = verifyPassword; + this.firstName = firstName; + this.lastName = lastName; + this.ipWhitelist = ipWhitelist; + this.authenticationModes = authenticationModes; + this.links = links; + } + + public Account(final String login, + final String email, + final String password, + final String firstName, + final String lastName, + final List ipWhitelist, + final List authenticationModes) { + this(login, email, password, password, firstName, lastName, ipWhitelist, authenticationModes, null); + } + + public Account(String firstName, String lastName, Links links) { + this(null, null, null, null, firstName, lastName, null, null, links); + } + + /** + * Account creation constructor + * + * @param email email + * @param firstName first name + * @param lastName last name + * @param password password + */ + public Account(String email, String password, String firstName, String lastName) { + this(email, email, password, password, firstName, lastName, null, null, null); + } + + /** + * Extract Account's ID from Account's URI + * + * @param uri Account's URI + * @return Account's ID extracted from URI + */ + public static String getId(String uri) { + return UriHelper.getLastUriPart(uri); + } + + public String getLogin() { + return login; + } + + public String getEmail() { + return email; + } + + public void setEmail(final String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(final String password) { + this.password = password; + } + + public String getVerifyPassword() { + return verifyPassword; + } + + public void setVerifyPassword(final String verifyPassword) { + this.verifyPassword = verifyPassword; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(final String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(final String lastName) { + this.lastName = lastName; + } + + @JsonIgnore + public String getUri() { + return links.getSelf(); + } + + @JsonIgnore + public String getProjectsUri() { + return links.getProjects(); + } + + @JsonIgnore + public String getId() { + return getId(getUri()); + } + + public List getIpWhitelist() { + return ipWhitelist; + } + + public void setIpWhitelist(final List ipWhitelist) { + this.ipWhitelist = ipWhitelist; + } + + public List getAuthenticationModes() { + return authenticationModes; + } + + public void setAuthenticationModes(final List authenticationModes) { + this.authenticationModes = authenticationModes; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this, "password", "verifyPassword"); + } + + /** + * Enumeration type representing GoodData authentication mode. + */ + public enum AuthenticationMode { + /** + * User can be authenticated using password + */ + PASSWORD, + /** + * User can be authenticated via SSO + */ + SSO + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Links { + private final String self; + private final String projects; + + @JsonCreator + public Links(@JsonProperty("self") String self, @JsonProperty("projects") String projects) { + this.self = self; + this.projects = projects; + } + + public String getSelf() { + return self; + } + + public String getProjects() { + return projects; + } + } + + /** + * Class representing update view of account + */ + public static class UpdateView { + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/Accounts.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/Accounts.java new file mode 100644 index 000000000..82c1a3c78 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/Accounts.java @@ -0,0 +1,34 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.account; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.collections.Page; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +/** + * List of accounts. Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = Id.NAME) +@JsonTypeName(Accounts.ROOT_NODE) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonDeserialize(using = AccountsDeserializer.class) +public class Accounts extends Page { + + static final String ROOT_NODE = "accountSettings"; + + + Accounts(final List items, final Paging paging, final Map links) { + super(items, paging, links); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/AccountsDeserializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/AccountsDeserializer.java new file mode 100644 index 000000000..f03c4b243 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/AccountsDeserializer.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.account; + +import com.gooddata.sdk.common.collections.PageDeserializer; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +class AccountsDeserializer extends PageDeserializer { + + protected AccountsDeserializer() { + super(Account.class); + } + + @Override + protected Accounts createPage(final List items, final Paging paging, final Map links) { + return new Accounts(items, paging, links); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/SeparatorSettings.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/SeparatorSettings.java new file mode 100644 index 000000000..3f94eabc3 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/account/SeparatorSettings.java @@ -0,0 +1,74 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.account; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Default thousand and decimal separator settings configured for a profile. + * Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("separators") +@JsonIgnoreProperties(ignoreUnknown = true) +public class SeparatorSettings implements Serializable { + + public static final String URI = "/gdc/account/profile/{id}/settings/separators"; + private static final long serialVersionUID = 446547615105910660L; + private final String thousand; + private final String decimal; + private final Links links; + + @JsonCreator + private SeparatorSettings( + @JsonProperty("thousand") final String thousand, + @JsonProperty("decimal") final String decimal, + @JsonProperty("links") final Links links) { + this.thousand = thousand; + this.decimal = decimal; + this.links = links; + } + + public String getThousand() { + return thousand; + } + + public String getDecimal() { + return decimal; + } + + public String getSelfLink() { + return links.self; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.toString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Links implements Serializable { + private final String self; + + @JsonCreator + private Links(@JsonProperty("self") final String self) { + this.self = self; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.toString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLog.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLog.java new file mode 100644 index 000000000..8bed61789 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLog.java @@ -0,0 +1,96 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.common.util.ISOZonedDateTime; + +import java.time.ZonedDateTime; + +/** + * Model class, used for special audit log events/access logs directly from haproxy, + * that represents access logs for particular hosts. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("accessLog") +@JsonIgnoreProperties(ignoreUnknown = true) +public class AccessLog { + + public static final String RESOURCE_URI = "/gdc/domains/{domainId}/accessLogs"; + + private final String id; + private final String host; + private final String path; + private final String method; + private final String code; + private final String size; + private final String userIp; + @ISOZonedDateTime + private final ZonedDateTime occurred; + @ISOZonedDateTime + private final ZonedDateTime recorded; + + @JsonCreator + public AccessLog(@JsonProperty("id") String id, @JsonProperty("host") String host, @JsonProperty("path") String path, + @JsonProperty("method") String method, @JsonProperty("code") String code, @JsonProperty("size") String size, + @JsonProperty("userIp") String userIp, @JsonProperty("occurred") ZonedDateTime occurred, @JsonProperty("recorded") ZonedDateTime recorded) { + this.id = id; + this.host = host; + this.path = path; + this.method = method; + this.code = code; + this.size = size; + this.userIp = userIp; + this.occurred = occurred; + this.recorded = recorded; + } + + public String getId() { + return id; + } + + public String getHost() { + return host; + } + + public String getPath() { + return path; + } + + public String getMethod() { + return method; + } + + public String getCode() { + return code; + } + + public String getSize() { + return size; + } + + public String getUserIp() { + return userIp; + } + + public ZonedDateTime getOccurred() { + return occurred; + } + + public ZonedDateTime getRecorded() { + return recorded; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogs.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogs.java new file mode 100644 index 000000000..b45655fc8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogs.java @@ -0,0 +1,39 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.collections.Page; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +@JsonTypeInfo( + include = JsonTypeInfo.As.WRAPPER_OBJECT, + use = JsonTypeInfo.Id.NAME +) +@JsonTypeName("accessLogs") +@JsonIgnoreProperties( + ignoreUnknown = true +) +@JsonSerialize( + using = AccessLogsSerializer.class +) +@JsonDeserialize( + using = AccessLogsDeserializer.class +) +public class AccessLogs extends Page { + static final String ROOT_NODE = "accessLogs"; + + public AccessLogs(List items, Paging paging, Map links) { + super(items, paging, links); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogsDeserializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogsDeserializer.java new file mode 100644 index 000000000..857547c4a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogsDeserializer.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.gooddata.sdk.common.collections.PageDeserializer; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +public class AccessLogsDeserializer extends PageDeserializer { + + public AccessLogsDeserializer() { + super(AccessLog.class); + } + + @Override + protected AccessLogs createPage(List items, Paging paging, Map links) { + return new AccessLogs(items, paging, links); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogsSerializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogsSerializer.java new file mode 100644 index 000000000..4c901162c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AccessLogsSerializer.java @@ -0,0 +1,14 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.gooddata.sdk.common.collections.PageSerializer; + +public class AccessLogsSerializer extends PageSerializer { + public AccessLogsSerializer() { + super(AccessLogs.ROOT_NODE); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEvent.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEvent.java new file mode 100644 index 000000000..554441574 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEvent.java @@ -0,0 +1,130 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.common.util.ISOZonedDateTime; + +import java.time.ZonedDateTime; +import java.util.Map; + +/** + * Audit event + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(AuditEvent.ROOT_NODE) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AuditEvent { + + public static final String GDC_URI = "/gdc"; + public static final String USER_URI = GDC_URI + "/account/profile/{userId}/auditEvents"; + public static final String ADMIN_URI = GDC_URI + "/domains/{domainId}/auditEvents"; + + static final String ROOT_NODE = "event"; + + private final String id; + + private final String userLogin; + + /** + * the time the event occurred + */ + @ISOZonedDateTime + private final ZonedDateTime occurred; + + /** + * the time event was recorded by audit system + */ + @ISOZonedDateTime + private final ZonedDateTime recorded; + + private final String userIp; + + private final boolean success; + + private final String type; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private final Map params; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private final Map links; + + @JsonCreator + public AuditEvent(@JsonProperty("id") String id, + @JsonProperty("userLogin") String userLogin, + @JsonProperty("occurred") ZonedDateTime occurred, + @JsonProperty("recorded") ZonedDateTime recorded, + @JsonProperty("userIp") String userIp, + @JsonProperty("success") boolean success, + @JsonProperty("type") String type, + @JsonProperty("params") Map params, + @JsonProperty("links") Map links) { + this.id = id; + this.userLogin = userLogin; + this.occurred = occurred; + this.recorded = recorded; + this.userIp = userIp; + this.success = success; + this.type = type; + this.params = params; + this.links = links; + } + + public String getId() { + return id; + } + + public String getUserLogin() { + return userLogin; + } + + /** + * the time the event occurred + */ + public ZonedDateTime getOccurred() { + return occurred; + } + + /** + * the time event was recorded by audit system + */ + public ZonedDateTime getRecorded() { + return recorded; + } + + public String getUserIp() { + return userIp; + } + + public boolean isSuccess() { + return success; + } + + public String getType() { + return type; + } + + public Map getParams() { + return params; + } + + public Map getLinks() { + return links; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEvents.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEvents.java new file mode 100644 index 000000000..e60324e1c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEvents.java @@ -0,0 +1,35 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.collections.Page; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +/** + * Pageable list DTO for Audit events + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(AuditEvents.ROOT_NODE) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSerialize(using = AuditEventsSerializer.class) +@JsonDeserialize(using = AuditEventsDeserializer.class) +public class AuditEvents extends Page { + + static final String ROOT_NODE = "events"; + + public AuditEvents(final List items, final Paging paging, final Map links) { + super(items, paging, links); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEventsDeserializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEventsDeserializer.java new file mode 100644 index 000000000..4ab1dd022 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEventsDeserializer.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.gooddata.sdk.common.collections.PageDeserializer; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +class AuditEventsDeserializer extends PageDeserializer { + + protected AuditEventsDeserializer() { + super(AuditEvent.class); + } + + @Override + protected AuditEvents createPage(final List items, final Paging paging, final Map links) { + return new AuditEvents(items, paging, links); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEventsSerializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEventsSerializer.java new file mode 100644 index 000000000..8d36cc8b3 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/auditevent/AuditEventsSerializer.java @@ -0,0 +1,15 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.auditevent; + +import com.gooddata.sdk.common.collections.PageSerializer; + +class AuditEventsSerializer extends PageSerializer { + + public AuditEventsSerializer() { + super(AuditEvents.ROOT_NODE); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ConnectorType.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ConnectorType.java new file mode 100644 index 000000000..c7b602e25 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ConnectorType.java @@ -0,0 +1,29 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +/** + * Enum containing implemented connector types (currently version 4 of Zendesk connector only). + */ +public enum ConnectorType { + + ZENDESK4(Zendesk4Settings.URL); + + //URL of the settings endpoint (which is not equal for individual connector types) + private final String settingsUrl; + + ConnectorType(String settingsUrl) { + this.settingsUrl = settingsUrl; + } + + public String getName() { + return name().toLowerCase(); + } + + public String getSettingsUrl() { + return settingsUrl; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Integration.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Integration.java new file mode 100644 index 000000000..4a5040878 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Integration.java @@ -0,0 +1,86 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Connector integration (i.e. one instance of configured ETL for loading of one GDC project). + */ +@JsonTypeName("integration") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Integration { + + public static final String URL = "/gdc/projects/{project}/connectors/{connector}/integration"; + private final IntegrationProcessStatus lastFinishedProcess; + private final IntegrationProcessStatus lastSuccessfulProcess; + private final IntegrationProcessStatus runningProcess; + private String projectTemplate; + private boolean active; + + public Integration(final String projectTemplate) { + this(projectTemplate, true, null, null, null); + } + + @JsonCreator + Integration(@JsonProperty("projectTemplate") String projectTemplate, @JsonProperty("active") boolean active, + @JsonProperty("lastFinishedProcess") IntegrationProcessStatus lastFinishedProcess, + @JsonProperty("lastSuccessfulProcess") IntegrationProcessStatus lastSuccessfulProcess, + @JsonProperty("runningProcess") IntegrationProcessStatus runningProcess) { + this.projectTemplate = notEmpty(projectTemplate, "projectTemplate"); + this.active = active; + this.lastFinishedProcess = lastFinishedProcess; + this.lastSuccessfulProcess = lastSuccessfulProcess; + this.runningProcess = runningProcess; + } + + public String getProjectTemplate() { + return projectTemplate; + } + + public void setProjectTemplate(final String projectTemplate) { + this.projectTemplate = notEmpty(projectTemplate, "projectTemplate"); + } + + public boolean isActive() { + return active; + } + + public void setActive(final boolean active) { + this.active = active; + } + + @JsonIgnore + public IntegrationProcessStatus getLastFinishedProcess() { + return lastFinishedProcess; + } + + @JsonIgnore + public IntegrationProcessStatus getLastSuccessfulProcess() { + return lastSuccessfulProcess; + } + + @JsonIgnore + public IntegrationProcessStatus getRunningProcess() { + return runningProcess; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/IntegrationProcessStatus.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/IntegrationProcessStatus.java new file mode 100644 index 000000000..df309dcbf --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/IntegrationProcessStatus.java @@ -0,0 +1,98 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.common.util.ISOZonedDateTime; +import com.gooddata.sdk.model.util.UriHelper; + +import java.time.ZonedDateTime; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Connector process (i.e. single ETL run) status used in integration object. Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class IntegrationProcessStatus { + + public static final String URI = "/gdc/projects/{project}/connectors/{connector}/integration/processes/{process}"; + private static final String SELF_LINK = "self"; + + private final Status status; + @ISOZonedDateTime + private final ZonedDateTime started; + @ISOZonedDateTime + private final ZonedDateTime finished; + private final Map links; + + @JsonCreator + protected IntegrationProcessStatus(@JsonProperty("status") Status status, + @JsonProperty("started") ZonedDateTime started, + @JsonProperty("finished") ZonedDateTime finished, + @JsonProperty("links") Map links) { + this.status = status; + this.started = started; + this.finished = finished; + this.links = links; + } + + public Status getStatus() { + return status; + } + + public ZonedDateTime getStarted() { + return started; + } + + public ZonedDateTime getFinished() { + return finished; + } + + /** + * Returns true when the connector process has already finished (no matter if it was successful). + * NOTE: It also returns false in case of inability to resolve the code (e.g. API change) + * + * @return true when the connector process has already finished, false otherwise + */ + @JsonIgnore + public boolean isFinished() { + return status != null && status.isFinished(); + } + + /** + * Returns true when the connector process failed. + * NOTE: It also returns false in case of inability to resolve the code (e.g. API change) + * + * @return true when the connector process failed, false otherwise + */ + @JsonIgnore + public boolean isFailed() { + return status != null && status.isFailed(); + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").get(SELF_LINK); + } + + @JsonIgnore + public String getId() { + return UriHelper.getLastUriPart(getUri()); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ProcessExecution.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ProcessExecution.java new file mode 100644 index 000000000..e1ec36c69 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ProcessExecution.java @@ -0,0 +1,26 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * Connector process execution (i.e. definition for single ETL run). Serialization only. + */ +@JsonTypeName("process") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public interface ProcessExecution { + + @JsonIgnore + ConnectorType getConnectorType(); + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ProcessStatus.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ProcessStatus.java new file mode 100644 index 000000000..a8665cbb6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/ProcessStatus.java @@ -0,0 +1,38 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.ISOZonedDateTimeDeserializer; + +import java.time.ZonedDateTime; +import java.util.Map; + +/** + * Connector process (i.e. single ETL run) status (standalone, not embedded in integration as its parent) . + * Deserialization only. + */ +@JsonTypeName("process") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProcessStatus extends IntegrationProcessStatus { + + public static final String URL = "/gdc/projects/{project}/connectors/{connector}/integration/processes"; + + @JsonCreator + ProcessStatus(@JsonProperty("status") Status status, + @JsonProperty("started") @JsonDeserialize(using = ISOZonedDateTimeDeserializer.class) ZonedDateTime started, //TODO check if necessary + @JsonProperty("finished") @JsonDeserialize(using = ISOZonedDateTimeDeserializer.class) ZonedDateTime finished, + @JsonProperty("links") Map links) { + super(status, started, finished, links); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Reload.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Reload.java new file mode 100644 index 000000000..cc9c46d08 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Reload.java @@ -0,0 +1,148 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import java.util.Map; +import java.util.Optional; + +/** + * Connector reload. + */ +@JsonTypeName("reload") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Reload { + + public static final String URL = "/gdc/projects/{project}/connectors/zendesk4/integration/reloads"; + + public static final String CHATS_START_TIME_PROPERTY = "chats"; + public static final String AGENT_TIMELINE_START_TIME_PROPERTY = "agentTimeline"; + + /** + * Reload was scheduled but not yet started. + */ + public static final String STATUS_DO = "DO"; + + /** + * Reload is running. + */ + public static final String STATUS_RUNNING = "RUNNING"; + + /** + * Reload was successfully finished. + */ + public static final String STATUS_FINISHED = "FINISHED"; + + /** + * Reload wasn't started because reload window was missed. + */ + public static final String STATUS_MISSED = "MISSED"; + + /** + * Reload finished with error. + */ + public static final String STATUS_ERROR = "ERROR"; + + private static final String SELF_LINK = "self"; + private static final String PROCESS_LINK = "process"; + private static final String INTEGRATION_LINK = "integration"; + + + private final Integer id; + + private final Map startTimes; + + private final String status; + + private final String processId; + + private final Map links; + + public Reload(Map startTimes) { + this(null, startTimes, null, null, null); + } + + @JsonCreator + public Reload( + @JsonProperty("id") final Integer id, + @JsonProperty("startTimes") final Map startTimes, + @JsonProperty("status") final String status, + @JsonProperty("processId") final String processId, + @JsonProperty("links") final Map links) { + this.id = id; + this.startTimes = startTimes; + this.status = status; + this.processId = processId; + this.links = links; + } + + public Integer getId() { + return id; + } + + public Map getStartTimes() { + return startTimes; + } + + @JsonIgnore + public Long getChatsStartTime() { + return startTimes.get(CHATS_START_TIME_PROPERTY); + } + + @JsonIgnore + public Long getAgentTimelineStartTime() { + return startTimes.get(AGENT_TIMELINE_START_TIME_PROPERTY); + } + + public String getStatus() { + return status; + } + + public String getProcessId() { + return processId; + } + + public Map getLinks() { + return links; + } + + /** + * @return URI to itself. + */ + @JsonIgnore + public Optional getUri() { + return getLink(SELF_LINK); + } + + /** + * @return URI to running process. Is empty if reload wasn't started yet. + */ + @JsonIgnore + public Optional getProcessUri() { + return getLink(PROCESS_LINK); + } + + /** + * @return URI to integration. + */ + @JsonIgnore + public Optional getIntegrationUri() { + return getLink(INTEGRATION_LINK); + } + + private Optional getLink(final String linkName) { + return links != null ? Optional.ofNullable(links.get(linkName)) : Optional.empty(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Settings.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Settings.java new file mode 100644 index 000000000..a7e2810ba --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Settings.java @@ -0,0 +1,26 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * Connector integration settings. + */ +@JsonTypeName("settings") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public interface Settings { + + @JsonIgnore + ConnectorType getConnectorType(); + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Status.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Status.java new file mode 100644 index 000000000..0144b3bb9 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Status.java @@ -0,0 +1,82 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.model.connector.Status.Code.ERROR; +import static com.gooddata.sdk.model.connector.Status.Code.SYNCHRONIZED; +import static com.gooddata.sdk.model.connector.Status.Code.USER_ERROR; + +/** + * Connector process status. Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Status { + private final String code; + private final String detail; + private final String description; + + @JsonCreator + Status(@JsonProperty("code") String code, @JsonProperty("detail") String detail, + @JsonProperty("description") String description) { + this.code = code; + this.detail = detail; + this.description = description; + } + + public String getCode() { + return code; + } + + public String getDetail() { + return detail; + } + + public String getDescription() { + return description; + } + + /** + * Returns true when the status means that the connector process has already finished + * (no matter if it was successful). + * NOTE: It also returns false in case of inability to resolve the code (e.g. API change) + * + * @return true when the status means that the connector process has already finished, false otherwise + */ + @JsonIgnore + public boolean isFinished() { + return SYNCHRONIZED.name().equalsIgnoreCase(code) || isFailed(); + } + + /** + * Returns true when the status means that the connector process failed. + * NOTE: It also returns false in case of inability to resolve the code (e.g. API change) + * + * @return true when the status means that the connector process failed, false otherwise + */ + @JsonIgnore + public boolean isFailed() { + return ERROR.name().equalsIgnoreCase(code) || USER_ERROR.name().equalsIgnoreCase(code); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * Enum of connector process status codes + */ + public enum Code { + NEW, SCHEDULED, DOWNLOADING, DOWNLOADED, TRANSFORMING, TRANSFORMED, UPLOADING, UPLOADED, SYNCHRONIZED, + ERROR, USER_ERROR + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Zendesk4ProcessExecution.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Zendesk4ProcessExecution.java new file mode 100644 index 000000000..197be0f1d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Zendesk4ProcessExecution.java @@ -0,0 +1,182 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.common.util.ISOZonedDateTimeSerializer; + +import java.time.ZonedDateTime; +import java.util.Map; +import java.util.TreeMap; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.model.connector.ConnectorType.ZENDESK4; + +/** + * Zendesk 4 (Insights) connector process execution (i.e. definition for single ETL run). Serialization only. + */ +public class Zendesk4ProcessExecution implements ProcessExecution { + + private Boolean incremental; + + private Boolean reload; + + private Boolean recoverable; + + private Boolean recoveryInProgress; + + private Map startTimes; + + private DownloadParams downloadParams; + + @Override + public ConnectorType getConnectorType() { + return ZENDESK4; + } + + public Boolean getIncremental() { + return incremental; + } + + public void setIncremental(final Boolean incremental) { + this.incremental = incremental; + } + + public Boolean getReload() { + return reload; + } + + /** + * set by scheduler, when the process is actually a reload of a project + */ + public void setReload(final Boolean reload) { + this.reload = reload; + } + + public Boolean getRecoverable() { + return recoverable; + } + + /** + * Tells if the newly started process should use recoverable feature. + * Usable from R176. + */ + public void setRecoverable(final Boolean recoverable) { + this.recoverable = recoverable; + } + + public Boolean getRecoveryInProgress() { + return recoveryInProgress; + } + + /** + * Tells if there is some recoverable process in progress for given project + * Usable from R176. + */ + public void setRecoveryInProgress(final Boolean recoveryInProgress) { + this.recoveryInProgress = recoveryInProgress; + } + + @JsonAnyGetter + @JsonSerialize(contentUsing = ISOZonedDateTimeSerializer.class) + public Map getStartTimes() { + return startTimes; + } + + + public void setStartTime(final String resource, final ZonedDateTime startTime) { + notEmpty(resource, "resource"); + notNull(startTime, "startTime"); + + startTimes = startTimes == null ? new TreeMap<>() : startTimes; + + startTimes.put(resource + "StartDate", startTime); + } + + @JsonIgnore + public DownloadParams getDownloadParams() { + if (downloadParams == null) { + downloadParams = new DownloadParams(); + } + return downloadParams; + } + + public void setDownloadParams(DownloadParams downloadParams) { + this.downloadParams = downloadParams; + } + + @JsonProperty("downloadParams") + private DownloadParams getDownloadParamsPlain() { + return downloadParams; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class DownloadParams { + private Boolean useBackup; + private Integer parallelWorkers; + private Integer parallelBatchSeconds; + + public DownloadParams(Boolean useBackup, Integer parallelWorkers, Integer parallelBatchSeconds) { + this.useBackup = useBackup; + this.parallelWorkers = parallelWorkers; + this.parallelBatchSeconds = parallelBatchSeconds; + } + + public DownloadParams(Integer parallelWorkers, Integer parallelBatchSeconds) { + this(null, parallelWorkers, parallelBatchSeconds); + } + + public DownloadParams(Boolean useBackup) { + this(useBackup, null, null); + } + + private DownloadParams() { + } + + @JsonProperty("useBackup") + public Boolean getUseBackup() { + return useBackup; + } + + public void setUseBackup(Boolean useBackup) { + this.useBackup = useBackup; + } + + @JsonProperty("parallelWorkers") + public Integer getParallelWorkers() { + return parallelWorkers; + } + + public void setParallelWorkers(Integer parallelWorkers) { + this.parallelWorkers = parallelWorkers; + } + + @JsonProperty("parallelBatchSeconds") + public Integer getParallelBatchSeconds() { + return parallelBatchSeconds; + } + + public void setParallelBatchSeconds(Integer parallelBatchSeconds) { + this.parallelBatchSeconds = parallelBatchSeconds; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Zendesk4Settings.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Zendesk4Settings.java new file mode 100644 index 000000000..287b69340 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/connector/Zendesk4Settings.java @@ -0,0 +1,97 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.connector; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.model.connector.ConnectorType.ZENDESK4; + +/** + * Zendesk 4 (Insights) connector settings. + */ +public class Zendesk4Settings implements Settings { + + public static final String URL = "/gdc/projects/{project}/connectors/zendesk4/integration/settings"; + private final String type; + private final String syncTime; + private final String syncTimeZone; + private String apiUrl; + private String zopimUrl; + private String account; + + public Zendesk4Settings(final String apiUrl) { + this(apiUrl, null, null, null, null, null); + } + + @JsonCreator + public Zendesk4Settings(@JsonProperty("apiUrl") String apiUrl, + @JsonProperty("zopimUrl") String zopimUrl, + @JsonProperty("account") String account, + @JsonProperty("type") String type, + @JsonProperty("syncTime") String syncTime, + @JsonProperty("syncTimeZone") String syncTimeZone) { + this.apiUrl = notEmpty(apiUrl, "apiUrl"); + this.zopimUrl = zopimUrl; + this.account = account; + this.type = type; + this.syncTime = syncTime; + this.syncTimeZone = syncTimeZone; + } + + public String getApiUrl() { + return apiUrl; + } + + public void setApiUrl(final String apiUrl) { + this.apiUrl = notEmpty(apiUrl, "apiUrl"); + } + + public String getZopimUrl() { + return zopimUrl; + } + + public void setZopimUrl(final String zopimUrl) { + this.zopimUrl = zopimUrl; + } + + public String getAccount() { + return account; + } + + public void setAccount(final String account) { + this.account = account; + } + + public String getType() { + return type; + } + + public String getSyncTime() { + return syncTime; + } + + public String getSyncTimeZone() { + return syncTimeZone; + } + + @Override + public ConnectorType getConnectorType() { + return ZENDESK4; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * Type of Zendesk account. + */ + public enum Zendesk4Type {plus, enterprise} +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/OutputStage.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/OutputStage.java new file mode 100644 index 000000000..314e3edee --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/OutputStage.java @@ -0,0 +1,144 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.warehouse.WarehouseSchema; + +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Output stage. + * For each project there is always one output stage, which always exists. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("outputStage") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class OutputStage { + + public static final String URI = "/gdc/dataload/projects/{id}/outputStage"; + + private static final String SELF_LINK = "self"; + private static final String OUTPUT_STAGE_DIFF = "outputStageDiff"; + private static final String DATALOAD_PROCESS = "dataloadProcess"; + private final Map links; + private String schema; + private String clientId; + private String outputStagePrefix; + + @JsonCreator + private OutputStage(@JsonProperty("schema") final String schema, + @JsonProperty("clientId") final String clientId, + @JsonProperty("outputStagePrefix") final String outputStagePrefix, + @JsonProperty("links") final Map links) { + this.schema = schema; + this.clientId = clientId; + this.outputStagePrefix = outputStagePrefix; + this.links = links; + } + + public Map getLinks() { + return links; + } + + /** + * get datawarehouse schema uri {@link WarehouseSchema} + * + * @return warehouse schema, can be null. + */ + @JsonProperty("schema") + public String getSchemaUri() { + return schema; + } + + @JsonProperty("schema") + public void setSchemaUri(final String schemaUri) { + this.schema = schemaUri; + } + + /** + * check if there is associated schema {@link WarehouseSchema} with this output stage + * + * @return true if there is associated schema, else false + */ + public boolean hasSchemaUri() { + return schema != null; + } + + /** + * get client ID + * + * @return client ID, can be null. + */ + public String getClientId() { + return clientId; + } + + public void setClientId(final String clientId) { + this.clientId = clientId; + } + + /** + * check if there is associated client id with this output stage + * + * @return true if there is associated client id, else false + */ + public boolean hasClientId() { + return clientId != null; + } + + /** + * get output stage prefix + * + * @return output stage prefix, can be null. + */ + public String getOutputStagePrefix() { + return outputStagePrefix; + } + + public void setOutputStagePrefix(final String outputStagePrefix) { + this.outputStagePrefix = outputStagePrefix; + } + + /** + * check if there is associated output stage prefix with this output stage + * + * @return true if there is associated output stage prefix, else false + */ + public boolean hasOutputStagePrefix() { + return outputStagePrefix != null; + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").get(SELF_LINK); + } + + @JsonIgnore + public String getOutputStageDiffUri() { + return notNullState(links, "links").get(OUTPUT_STAGE_DIFF); + } + + @JsonIgnore + public String getDataloadProcessUri() { + return notNullState(links, "links").get(DATALOAD_PROCESS); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/AsyncTask.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/AsyncTask.java new file mode 100644 index 000000000..5d145923d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/AsyncTask.java @@ -0,0 +1,58 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * Asynchronous task containing link for polling. + * This task differs from {@link com.gooddata.sdk.model.gdc.AsyncTask} in links field. + * Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("asyncTask") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AsyncTask { + + @JsonProperty + private final Links links; + + @JsonCreator + private AsyncTask(@JsonProperty("links") Links links) { + this.links = links; + } + + public AsyncTask(final String uri) { + this.links = new Links(uri); + } + + @JsonIgnore + public String getUri() { + return links.getPoll(); + } + + private static class Links { + + private final String poll; + + @JsonCreator + private Links(@JsonProperty("poll") String poll) { + this.poll = poll; + } + + public String getPoll() { + return poll; + } + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/DataloadProcess.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/DataloadProcess.java new file mode 100644 index 000000000..689453d40 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/DataloadProcess.java @@ -0,0 +1,136 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.util.UriHelper; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Dataload process. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("process") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DataloadProcess { + + public static final String URI = "/gdc/projects/{projectId}/dataload/processes/{processId}"; + + private static final String SELF_LINK = "self"; + private static final String EXECUTIONS_LINK = "executions"; + + private String name; + private String type; + private Set executables; + private Map links; + private String path; + + public DataloadProcess(String name, String type) { + this.name = notEmpty(name, "name"); + this.type = notEmpty(type, "type"); + } + + /** + * Use this constructor, when you want to deploy process from appstore. + * + * @param name name + * @param type type + * @param appstorePath valid path to brick in appstore + */ + public DataloadProcess(String name, String type, String appstorePath) { + this(name, type); + this.path = appstorePath; + } + + public DataloadProcess(String name, ProcessType type) { + this(name, notNull(type, "type").toString()); + } + + @JsonCreator + private DataloadProcess(@JsonProperty("name") String name, @JsonProperty("type") String type, + @JsonProperty("executables") Set executables, + @JsonProperty("links") Map links) { + this(name, type); + this.executables = executables != null ? Collections.unmodifiableSet(executables) : null; + this.links = links; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + @JsonIgnore + public Set getExecutables() { + return executables; + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").get(SELF_LINK); + } + + @JsonIgnore + public String getId() { + return UriHelper.getLastUriPart(getUri()); + } + + @JsonIgnore + public String getExecutionsUri() { + return notNullState(links, "links").get(EXECUTIONS_LINK); + } + + @JsonIgnore + public String getSourceUri() { + return getUri() + "/source"; + } + + public void validateExecutable(final String executable) { + if (getExecutables() != null && !getExecutables().isEmpty() && + !getExecutables().contains(executable)) { + throw new IllegalArgumentException("Executable " + executable + " not found in process executables " + getExecutables()); + } + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/DataloadProcesses.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/DataloadProcesses.java new file mode 100644 index 000000000..307eed7bf --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/DataloadProcesses.java @@ -0,0 +1,45 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.account.Account; + +import java.util.Collection; +import java.util.List; + +/** + * List of dataload processes. Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("processes") +@JsonIgnoreProperties(ignoreUnknown = true) +public class DataloadProcesses { + public static final String URI = "/gdc/projects/{projectId}/dataload/processes"; + + public static final String USER_PROCESSES_URI = Account.URI + "/dataload/processes"; + + private final List items; + + @JsonCreator + DataloadProcesses(@JsonProperty("items") List items) { + this.items = items; + } + + public Collection getItems() { + return items; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecution.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecution.java new file mode 100644 index 000000000..9a8a064f0 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecution.java @@ -0,0 +1,73 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.HashMap; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Dataload process execution. Serialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("execution") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProcessExecution { + + private final String executionsUri; + + private final String executable; + private final Map params; + private final Map hiddenParams; + + public ProcessExecution(DataloadProcess process, String executable) { + this(process, executable, new HashMap<>(), new HashMap<>()); + } + + public ProcessExecution(DataloadProcess process, String executable, Map params) { + this(process, executable, params, new HashMap<>()); + } + + public ProcessExecution(DataloadProcess process, String executable, Map params, Map hiddenParams) { + notNull(process, "process"); + this.executionsUri = notEmpty(process.getExecutionsUri(), "process executions link"); + this.executable = executable; + this.params = notNull(params, "params"); + this.hiddenParams = notNull(hiddenParams, "hiddenParams"); + + process.validateExecutable(executable); + } + + public String getExecutable() { + return executable; + } + + public Map getParams() { + return params; + } + + public Map getHiddenParams() { + return hiddenParams; + } + + @JsonIgnore + public String getExecutionsUri() { + return executionsUri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this, "hiddenParams", "executionsUri"); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecutionDetail.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecutionDetail.java new file mode 100644 index 000000000..57b9b8c6f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecutionDetail.java @@ -0,0 +1,121 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.gdc.ErrorStructure; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.common.util.ISOZonedDateTime; + +import java.net.URI; +import java.time.ZonedDateTime; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Dataload process execution detail. Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("executionDetail") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProcessExecutionDetail { + + private static final String LOG_LINK = "log"; + private static final String SELF_LINK = "self"; + private static final String EXECUTION_LINK = "poll"; + private static final String STATUS_OK = "OK"; + private final String status; + + @ISOZonedDateTime + private final ZonedDateTime created; + @ISOZonedDateTime + private final ZonedDateTime started; + @ISOZonedDateTime + private final ZonedDateTime updated; + @ISOZonedDateTime + private final ZonedDateTime finished; + + private final ErrorStructure error; + private final Map links; + + @JsonCreator + private ProcessExecutionDetail(@JsonProperty("status") String status, + @JsonProperty("created") ZonedDateTime created, + @JsonProperty("started") ZonedDateTime started, + @JsonProperty("updated") ZonedDateTime updated, + @JsonProperty("finished") ZonedDateTime finished, + @JsonProperty("error") ErrorStructure error, + @JsonProperty("links") Map links) { + this.status = notEmpty(status, "status"); + this.created = notNull(created, "created"); + this.started = started; + this.updated = updated; + this.finished = finished; + this.error = error; + this.links = links; + } + + public static URI uriFromExecutionUri(URI executionUri) { + return URI.create(executionUri.toString() + "/detail"); + } + + public String getStatus() { + return status; + } + + public ZonedDateTime getCreated() { + return created; + } + + public ZonedDateTime getStarted() { + return started; + } + + public ZonedDateTime getUpdated() { + return updated; + } + + public ZonedDateTime getFinished() { + return finished; + } + + public ErrorStructure getError() { + return error; + } + + @JsonIgnore + public String getLogUri() { + return notNullState(links, "links").get(LOG_LINK); + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").get(SELF_LINK); + } + + @JsonIgnore + public String getExecutionUri() { + return notNullState(links, "links").get(EXECUTION_LINK); + } + + @JsonIgnore + public boolean isSuccess() { + return STATUS_OK.equals(status); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecutionTask.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecutionTask.java new file mode 100644 index 000000000..8fe5169ec --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessExecutionTask.java @@ -0,0 +1,43 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Process execution task. Deserialization only + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("executionTask") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProcessExecutionTask { + + private static final String POLL_LINK = "poll"; + private static final String DETAIL_LINK = "detail"; + + private final Map links; + + @JsonCreator + private ProcessExecutionTask(@JsonProperty("links") Map links) { + this.links = links; + } + + public String getPollUri() { + return notNullState(links, "links").get(POLL_LINK); + } + + public String getDetailUri() { + return notNullState(links, "links").get(DETAIL_LINK); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessType.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessType.java new file mode 100644 index 000000000..1d32f2a12 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ProcessType.java @@ -0,0 +1,17 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +/** + * Represents type of dataload process. Please note that this enumeration must not be complete. + */ +public enum ProcessType { + GRAPH, + RUBY, + JAVASCRIPT, + GROOVY, + DATALOAD +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/Schedule.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/Schedule.java new file mode 100644 index 000000000..e7e113c3c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/Schedule.java @@ -0,0 +1,269 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.common.util.ISOZonedDateTimeDeserializer; +import com.gooddata.sdk.model.util.UriHelper; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Schedule. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("schedule") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Schedule { + + public static final String URI = "/gdc/projects/{projectId}/schedules/{scheduleId}"; + + private static final String SELF_LINK = "self"; + private static final String EXECUTIONS_LINK = "executions"; + + private static final String MSETL_TYPE = "MSETL"; + private static final String PROCESS_ID = "PROCESS_ID"; + private static final String EXECUTABLE = "EXECUTABLE"; + + private final String type; + private final ZonedDateTime nextExecutionTime; + private final int consecutiveFailedExecutionCount; + private final Map params; + private final Map links; + private String state; + private String cron; + private String timezone; + private Integer reschedule; + private String triggerScheduleId; + private String name; + + public Schedule(final DataloadProcess process, final String executable, final String cron) { + this(process, executable); + this.cron = notEmpty(cron, "cron"); + } + + /** + * Creates schedule, which is triggered by execution of different schedule + * + * @param process process to create schedule for + * @param executable executable to be scheduled for execution + * @param triggerSchedule schedule, which will trigger created schedule + */ + public Schedule(final DataloadProcess process, final String executable, final Schedule triggerSchedule) { + this(process, executable); + this.triggerScheduleId = notEmpty(notNull(triggerSchedule, "triggerSchedule").getId(), "triggerSchedule ID"); + } + + private Schedule(final DataloadProcess process, final String executable) { + notNull(process, "process"); + + this.type = MSETL_TYPE; + this.state = ScheduleState.ENABLED.name(); + this.params = new HashMap<>(); + this.params.put(PROCESS_ID, process.getId()); + process.validateExecutable(executable); + this.nextExecutionTime = null; + this.consecutiveFailedExecutionCount = 0; + this.params.put(EXECUTABLE, executable); + this.links = Collections.emptyMap(); + } + + @JsonCreator + private Schedule(@JsonProperty("type") final String type, + @JsonProperty("state") final String state, + @JsonProperty("cron") final String cron, + @JsonProperty("timezone") final String timezone, + @JsonProperty("nextExecutionTime") @JsonDeserialize(using = ISOZonedDateTimeDeserializer.class) ZonedDateTime nextExecutionTime, + @JsonProperty("consecutiveFailedExecutionCount") final int consecutiveFailedExecutionCount, + @JsonProperty("params") final Map params, + @JsonProperty("reschedule") final Integer reschedule, + @JsonProperty("triggerScheduleId") final String triggerScheduleId, + @JsonProperty("name") final String name, + @JsonProperty("links") final Map links) { + this.type = type; + this.state = state; + this.cron = cron; + this.timezone = timezone; + this.nextExecutionTime = nextExecutionTime; + this.consecutiveFailedExecutionCount = consecutiveFailedExecutionCount; + this.params = params; + this.reschedule = reschedule; + this.triggerScheduleId = triggerScheduleId; + this.name = name; + this.links = links; + } + + public String getType() { + return type; + } + + public String getState() { + return state; + } + + public void setState(final String state) { + this.state = notEmpty(state, "state"); + } + + @JsonIgnore + public void setState(final ScheduleState state) { + this.state = notNull(state, "state").name(); + } + + @JsonIgnore + public boolean isEnabled() { + return ScheduleState.ENABLED.name().equals(state); + } + + @JsonIgnore + public String getProcessId() { + return params.get(PROCESS_ID); + } + + public void setProcessId(final DataloadProcess process) { + params.put(PROCESS_ID, process.getId()); + } + + @JsonIgnore + public String getExecutable() { + return params.get(EXECUTABLE); + } + + public void setExecutable(final DataloadProcess process, final String executable) { + notNull(executable, "executable"); + notNull(process, "process").validateExecutable(executable); + params.put(EXECUTABLE, executable); + } + + public Map getParams() { + return Collections.unmodifiableMap(params); + } + + public void addParam(String key, String value) { + notEmpty(key, "param"); + notNull(value, "value"); + params.put(key, value); + } + + public void removeParam(String paramKey) { + notEmpty(paramKey, "paramKey!"); + params.remove(paramKey); + } + + + public String getCron() { + return cron; + } + + public void setCron(final String cron) { + this.cron = notEmpty(cron, "cron"); + } + + public String getTimezone() { + return timezone; + } + + public void setTimezone(final String timezone) { + this.timezone = timezone; + } + + @JsonIgnore + public void setTimezone(final ZonedDateTime timezone) { + this.timezone = notNull(timezone, "timezone").getZone().getId(); + } + + /** + * Duration after a failed execution of the schedule is executed again + * + * @return reschedule duration in minutes + */ + @JsonProperty("reschedule") + public Integer getRescheduleInMinutes() { + return reschedule; + } + + /** + * Duration after a failed execution of the schedule is executed again + * + * @return reschedule duration in minutes + */ + @JsonIgnore + public Duration getReschedule() { + return reschedule != null ? Duration.ofMinutes(getRescheduleInMinutes()) : null; + } + + /** + * Duration after a failed execution of the schedule is executed again + * + * @param reschedule this duration should not be too low, because it can be rejected by REST API (e.g. 15 minutes or more) + */ + public void setReschedule(Duration reschedule) { + this.reschedule = Long.valueOf(notNull(reschedule, "reschedule").toMinutes()).intValue(); + } + + public String getTriggerScheduleId() { + return triggerScheduleId; + } + + public void setTriggerScheduleId(final String triggerScheduleId) { + this.triggerScheduleId = triggerScheduleId; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + @JsonIgnore + public ZonedDateTime getNextExecutionTime() { + return nextExecutionTime; + } + + @JsonIgnore + public int getConsecutiveFailedExecutionCount() { + return consecutiveFailedExecutionCount; + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").get(SELF_LINK); + } + + @JsonIgnore + public String getId() { + return UriHelper.getLastUriPart(getUri()); + } + + @JsonIgnore + public String getExecutionsUri() { + return notNullState(links, "links").get(EXECUTIONS_LINK); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ScheduleExecution.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ScheduleExecution.java new file mode 100644 index 000000000..2ff82dd9a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ScheduleExecution.java @@ -0,0 +1,94 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.ISOZonedDateTimeDeserializer; + +import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Schedule execution + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("execution") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ScheduleExecution { + + public static final String URI = "/gdc/projects/{projectId}/schedules/{scheduleId}/executions/{executionId}"; + private static final Set FINISHED_STATUSES = new HashSet<>(Arrays.asList("OK", "ERROR", "CANCELED", "TIMEOUT")); + + private ZonedDateTime created; + private String status; + private String trigger; + private String processLastDeployedBy; + + private Map links; + + public ScheduleExecution() { + } + + @JsonCreator + private ScheduleExecution(@JsonProperty("createdTime") @JsonDeserialize(using = ISOZonedDateTimeDeserializer.class) ZonedDateTime created, + @JsonProperty("status") String executionStatus, + @JsonProperty("trigger") String trigger, + @JsonProperty("processLastDeployedBy") String processLastDeployedBy, + @JsonProperty("links") Map links) { + this.created = created; + this.status = executionStatus; + this.trigger = trigger; + this.processLastDeployedBy = processLastDeployedBy; + this.links = links; + } + + public String getStatus() { + return status; + } + + public Map getLinks() { + return links; + } + + public ZonedDateTime getCreated() { + return created; + } + + public String getTrigger() { + return trigger; + } + + public String getProcessLastDeployedBy() { + return processLastDeployedBy; + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").get("self"); + } + + /** + * + * @return whether schedule execution is finished + */ + @JsonIgnore + public boolean isFinished() { + return FINISHED_STATUSES.contains(getStatus()); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ScheduleState.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ScheduleState.java new file mode 100644 index 000000000..553d6632e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/ScheduleState.java @@ -0,0 +1,13 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +/** + * Dataload schedule state. + */ +public enum ScheduleState { + ENABLED, DISABLED +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/Schedules.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/Schedules.java new file mode 100644 index 000000000..2443ff04e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/Schedules.java @@ -0,0 +1,30 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.collections.Page; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; + +/** + * List of schedules. Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("schedules") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonDeserialize(using = SchedulesDeserializer.class) +public class Schedules extends Page { + public static final String URI = "/gdc/projects/{projectId}/schedules"; + + Schedules(final List items, final Paging paging) { + super(items, paging); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/SchedulesDeserializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/SchedulesDeserializer.java new file mode 100644 index 000000000..7c21903e1 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataload/processes/SchedulesDeserializer.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataload.processes; + +import com.gooddata.sdk.common.collections.PageDeserializer; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +class SchedulesDeserializer extends PageDeserializer { + + protected SchedulesDeserializer() { + super(Schedule.class); + } + + @Override + protected Schedules createPage(final List items, final Paging paging, final Map links) { + return new Schedules(items, paging); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetLinks.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetLinks.java new file mode 100644 index 000000000..3b0027c49 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetLinks.java @@ -0,0 +1,28 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.gdc.AboutLinks; + +import java.util.List; + +/** + * Dataset links. + * Deserialization only. + */ +public class DatasetLinks extends AboutLinks { + + public static final String URI = "/gdc/md/{project}/ldm/singleloadinterface"; + + @JsonCreator + public DatasetLinks(@JsonProperty("category") String category, @JsonProperty("summary") String summary, + @JsonProperty("links") List links) { + super(category, summary, null, links); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetManifest.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetManifest.java new file mode 100644 index 000000000..63563f952 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetManifest.java @@ -0,0 +1,215 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.BooleanIntegerSerializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + + +/** + * Dataset specific upload manifest + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("dataSetSLIManifest") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DatasetManifest { + + public static final String URI = "/gdc/md/{projectId}/ldm/singleloadinterface/{dataSet}/manifest"; + + private final String dataSet; + private String file; + private List parts; + private InputStream source; + + public DatasetManifest(String dataSet) { + this.dataSet = dataSet; + } + + /** + * Create dataset upload manifest. + * + * @param dataSet dataset name + * @param source source CSV + */ + public DatasetManifest(final String dataSet, final InputStream source) { + this.source = notNull(source, "source"); + this.dataSet = notEmpty(dataSet, "dataSet"); + } + + @JsonCreator + public DatasetManifest(@JsonProperty("dataSet") String dataSet, @JsonProperty("file") String file, + @JsonProperty("parts") List parts) { + this.dataSet = dataSet; + this.file = file; + this.parts = parts; + } + + public String getDataSet() { + return dataSet; + } + + public List getParts() { + return parts; + } + + public void setParts(List parts) { + this.parts = parts; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = notEmpty(file, "file"); + } + + /** + * Set upload mode for all parts of this dataset manifest + * + * @param uploadMode upload mode + */ + public void setUploadMode(final UploadMode uploadMode) { + notNull(uploadMode, "uploadMode"); + for (Part part : parts) { + part.setUploadMode(uploadMode.toString()); + } + } + + /** + * Map the given CSV column name to the dataset field + * + * @param columnName column name + * @param populates dataset field + */ + public void setMapping(final String columnName, final String populates) { + notNull(columnName, "columnName"); + notNull(populates, "populates"); + for (Part part : parts) { + if (part.getPopulates() == null || part.getPopulates().size() != 1) { + throw new IllegalStateException("Only parts with exactly one populates are supported " + part.getPopulates()); + } + final String field = part.getPopulates().iterator().next(); + if (populates.equals(field)) { + part.setColumnName(columnName); + return; + } + } + throw new IllegalArgumentException("Dataset manifest parts doesn't contain populate value " + populates); + } + + @JsonIgnore + public InputStream getSource() { + return source; + } + + @JsonIgnore + public void setSource(final InputStream source) { + this.source = notNull(source, "source"); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this, "source"); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class Part { + + @JsonProperty("mode") + private String uploadMode; + private String columnName; + private List populates; + private Boolean referenceKey; + private Map constraints; + + @JsonCreator + public Part(@JsonProperty("mode") String uploadMode, + @JsonProperty("columnName") String columnName, + @JsonProperty("populates") List populates, + @JsonProperty("referenceKey") @JsonDeserialize(using = BooleanDeserializer.class) Boolean referenceKey, + @JsonProperty("constraints") Map constraints) { + this.uploadMode = uploadMode; + this.columnName = columnName; + this.populates = populates; + this.referenceKey = referenceKey; + this.constraints = constraints; + } + + public String getUploadMode() { + return uploadMode; + } + + public void setUploadMode(String uploadMode) { + this.uploadMode = uploadMode; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + + public List getPopulates() { + return populates; + } + + public void setPopulates(List populates) { + this.populates = populates; + } + + /** + * @return true if the referenceKey is set and is true + */ + @JsonIgnore + public boolean isReferenceKey() { + return Boolean.TRUE.equals(referenceKey); + } + + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean getReferenceKey() { + return referenceKey; + } + + public void setReferenceKey(boolean referenceKey) { + this.referenceKey = referenceKey; + } + + public Map getConstraints() { + return constraints; + } + + public void setConstraints(Map constraints) { + this.constraints = constraints; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetManifests.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetManifests.java new file mode 100644 index 000000000..41dade477 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetManifests.java @@ -0,0 +1,42 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Encapsulates list of {@link DatasetManifest}. + */ +public class DatasetManifests { + + private final Collection manifests; + + /** + * Construct object. + * + * @param manifests dataset upload manifests + */ + @JsonCreator + public DatasetManifests(@JsonProperty("dataSetSLIManifestList") Collection manifests) { + this.manifests = notNull(manifests, "manifests"); + } + + @JsonProperty("dataSetSLIManifestList") + public Collection getManifests() { + return manifests; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetNotFoundException.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetNotFoundException.java new file mode 100644 index 000000000..32704688a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/DatasetNotFoundException.java @@ -0,0 +1,26 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.gooddata.sdk.common.GoodDataException; + +import static java.lang.String.format; + +/** + * Represents error when dataset of the given id was not found + */ +public class DatasetNotFoundException extends GoodDataException { + + private static final String MESSAGE = "Dataset %s was not found"; + + public DatasetNotFoundException(String dataset) { + super(format(MESSAGE, dataset)); + } + + public DatasetNotFoundException(String dataset, Throwable cause) { + super(format(MESSAGE, dataset), cause); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/EtlMode.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/EtlMode.java new file mode 100644 index 000000000..4299adde8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/EtlMode.java @@ -0,0 +1,47 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +@JsonTypeName("etlMode") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EtlMode { + + public static final String URL = "/gdc/md/{project}/etl/mode"; + + private final EtlModeType mode; + + private final LookupMode lookup; + + @JsonCreator + public EtlMode(@JsonProperty("mode") final EtlModeType mode, + @JsonProperty("lookup") final LookupMode lookup) { + this.mode = mode; + this.lookup = lookup; + } + + public EtlModeType getMode() { + return mode; + } + + public LookupMode getLookup() { + return lookup; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/EtlModeType.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/EtlModeType.java new file mode 100644 index 000000000..8492b9898 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/EtlModeType.java @@ -0,0 +1,21 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Enum containing ETL mode types. + */ +public enum EtlModeType { + + SLI, DLI, VOID; + + @JsonValue + public String getName() { + return name(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/LookupMode.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/LookupMode.java new file mode 100644 index 000000000..2f39b6118 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/LookupMode.java @@ -0,0 +1,21 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Enum containing ETL lookup modes. + */ +public enum LookupMode { + + RECREATE; + + @JsonValue + public String getName() { + return name().toLowerCase(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/MaqlDml.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/MaqlDml.java new file mode 100644 index 000000000..fac9510fb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/MaqlDml.java @@ -0,0 +1,21 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.gooddata.sdk.model.gdc.AbstractMaql; + +/** + * MAQL DML statement. + * Serialization only. + */ +public class MaqlDml extends AbstractMaql { + + public static final String URI = "/gdc/md/{project}/dml/manage"; + + public MaqlDml(final String maql) { + super(maql); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Pull.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Pull.java new file mode 100644 index 000000000..56eeaf5b7 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Pull.java @@ -0,0 +1,37 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * ETL Pull input DTO (for internal use). + * Serialization only. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Pull { + + public static final String URI = "/gdc/md/{projectId}/etl/pull2"; + + @JsonProperty("pullIntegration") + private final String remoteDir; + + + public Pull(String remoteDir) { + this.remoteDir = remoteDir; + } + + public String getRemoteDir() { + return remoteDir; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/PullTask.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/PullTask.java new file mode 100644 index 000000000..f1aedb205 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/PullTask.java @@ -0,0 +1,53 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Asynchronous ETL Pull 2 task (for internal use). + * Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("pull2Task") +@JsonIgnoreProperties(ignoreUnknown = true) +public class PullTask { + + public static final String URI = "/gdc/md/{projectId}/tasks/task/{taskId}"; + + private final Links links; + + @JsonCreator + private PullTask(@JsonProperty("links") Links links) { + notNull(links, "links"); + + this.links = links; + } + + public String getPollUri() { + return links.uri; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Links { + + private final String uri; + + @JsonCreator + private Links(@JsonProperty("poll") String uri) { + notEmpty(uri, "uri"); + + this.uri = uri; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/TaskState.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/TaskState.java new file mode 100644 index 000000000..e65700f15 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/TaskState.java @@ -0,0 +1,59 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Update Project Data task status (for internal use). + * Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("taskState") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class TaskState { + + private static final String OK = "OK"; + private static final String ERROR = "ERROR"; + + private final String message; + + private final String status; + + @JsonCreator + public TaskState(@JsonProperty("status") final String status, @JsonProperty("msg") final String message) { + this.message = message; + this.status = status; + } + + public String getMessage() { + return message; + } + + public String getStatus() { + return status; + } + + public boolean isSuccess() { + return OK.equals(status); + } + + public boolean isFinished() { + return OK.equals(status) || ERROR.equals(status); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Upload.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Upload.java new file mode 100644 index 000000000..98c3d38db --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Upload.java @@ -0,0 +1,129 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.GDZonedDateTimeDeserializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.time.ZonedDateTime; + +/** + * Contains information about single dataset upload. + * Deserialization only. + */ +@JsonTypeName("dataUpload") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Upload { + + public static final String URI = "/gdc/md/{projectId}/data/upload/{uploadId}"; + + private final String uri; + private final String status; + private final double progress; + private final String message; + private final UploadMode uploadMode; + private final Integer size; + private final ZonedDateTime createdAt; + private final ZonedDateTime processedAt; + + Upload(@JsonProperty("msg") String message, + @JsonProperty("progress") Double progress, + @JsonProperty("status") String status, + @JsonProperty("fullUpload") @JsonDeserialize(using = BooleanDeserializer.class) Boolean fullUpload, + @JsonProperty("uri") String uri, + @JsonProperty("createdAt") @JsonDeserialize(using = GDZonedDateTimeDeserializer.class) ZonedDateTime createdAt, + @JsonProperty("fileSize") Integer size, + @JsonProperty("processedAt") @JsonDeserialize(using = GDZonedDateTimeDeserializer.class) ZonedDateTime processedAt) { + + this.uri = uri; + this.status = status; + this.progress = progress != null ? progress : 0; + this.message = message; + this.uploadMode = toUploadMode(fullUpload); + this.size = size; + this.createdAt = createdAt; + this.processedAt = processedAt; + } + + /** + * @return uri link to self + */ + public String getUri() { + return uri; + } + + /** + * @return upload status + */ + public String getStatus() { + return status; + } + + /** + * @return upload progress in percent as floating point number + */ + public double getProgress() { + return progress; + } + + /** + * @return error message if the upload failed, {@code null} otherwise + */ + public String getMessage() { + return message; + } + + /** + * @return {@link UploadMode} of this upload + */ + public UploadMode getUploadMode() { + return uploadMode; + } + + /** + * @return size of the data uploaded by this upload + */ + public Integer getSize() { + return size; + } + + /** + * @return date of creation of this upload + */ + public ZonedDateTime getCreatedAt() { + return createdAt; + } + + /** + * @return date when the upload was processed or {@code null} if upload is still being processed + */ + public ZonedDateTime getProcessedAt() { + return processedAt; + } + + /** + * Converts boolean value containing if the upload is full or incremental load to {@link UploadMode} enum. + */ + private UploadMode toUploadMode(Boolean fullUpload) { + if (fullUpload == null) { + return null; + } + + return fullUpload ? UploadMode.FULL : UploadMode.INCREMENTAL; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadMode.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadMode.java new file mode 100644 index 000000000..2c0c67d61 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadMode.java @@ -0,0 +1,64 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import java.util.HashMap; +import java.util.Map; + +/** + * Upload modes enum. + */ +public enum UploadMode { + /** + * Dataset input for loading in full mode (reload from scratch) + */ + FULL("FULL"), + /** + * Dataset input for loading data incrementally + */ + INCREMENTAL("INCREMENTAL"), + /** + * Dataset input for loading data by calculating based on its attributes. + * Example: last sync, max timestamp, having timestamp field,... + */ + DEFAULT("DEFAULT"), + /** + * Dataset input for deleting + */ + DELETE_CENTER("DELETE-CENTER"); + + private static final Map lookup = new HashMap<>(); + + static { + for (UploadMode m : UploadMode.values()) { + lookup.put(m.modeStr, m); + } + } + + private final String modeStr; + + /** + * @param modeStr string representation of the upload mode + */ + UploadMode(final String modeStr) { + this.modeStr = modeStr; + } + + /** + * Lookup method. + * + * @param mode a string value passed in the constructor + * @return UploadMode enum instance according to the given mode string + */ + public static UploadMode get(String mode) { + return lookup.get(mode); + } + + @Override + public String toString() { + return this.modeStr; + } +} \ No newline at end of file diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadStatistics.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadStatistics.java new file mode 100644 index 000000000..540d76653 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadStatistics.java @@ -0,0 +1,49 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.HashMap; +import java.util.Map; + +/** + * Global statistics about project's uploads + */ +@JsonTypeName("dataUploadsInfo") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class UploadStatistics { + + public static final String URI = "/gdc/md/{projectId}/data/uploads_info"; + + private final Map statusesCount; + + private UploadStatistics(@JsonProperty("statusesCount") Map statusesCount) { + this.statusesCount = new HashMap<>(statusesCount); + } + + /** + * Returns count of uploads finished in given status. + * + * @param uploadStatus status of uploads to be counted + * @return count of uploads or zero when statistics for given status don't exist + */ + public int getUploadsCount(String uploadStatus) { + final Integer uploadsCount = statusesCount.get(uploadStatus); + + return uploadsCount != null ? uploadsCount : 0; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Uploads.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Uploads.java new file mode 100644 index 000000000..1b44c8735 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/Uploads.java @@ -0,0 +1,42 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +/** + * Contains collection of dataset uploads. + * Deserialization only. For internal use only. + */ +@JsonTypeName("dataUploads") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Uploads { + + private final Collection uploads; + + Uploads(@JsonProperty("uploads") Collection uploads) { + this.uploads = uploads; + } + + /** + * @return all items of uploads collection + */ + public Collection items() { + return uploads; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadsInfo.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadsInfo.java new file mode 100644 index 000000000..80b8dcdd8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/dataset/UploadsInfo.java @@ -0,0 +1,139 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.dataset; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Meta; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Contains information about dataset uploads for every single dataset in the project. + * For more about dataset uploads information, see {@link DataSet}. + * Deserialization only. Internal use only + */ +@JsonTypeName("dataSetsInfo") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class UploadsInfo { + + public static final String URI = "/gdc/md/{projectId}/data/sets"; + + private final Map datasets = new HashMap<>(); + + //for deserialization and testing purposes only + UploadsInfo(@JsonProperty("sets") Collection datasets) { + if (datasets != null) { + for (DataSet dataset : datasets) { + this.datasets.put(dataset.getDatasetId(), dataset); + } + } + } + + /** + * Returns dataset uploads information for a dataset with the given ID. + * + * @param datasetId dataset identifier + * @return {@link DataSet} object + */ + public DataSet getDataSet(String datasetId) { + notEmpty(datasetId, "datasetId"); + + final DataSet dataSet = datasets.get(datasetId); + if (dataSet != null) { + return dataSet; + } else { + throw new DatasetNotFoundException(datasetId); + } + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * Contains information about dataset uploads for one dataset in the project. Information contain: + *

+ *

+ * Deserialization only. + */ + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DataSet { + + private final Meta meta; + private final String datasetUploadsUri; + private final LastUpload lastUpload; + + private DataSet( + @JsonProperty("meta") Meta meta, + @JsonProperty("dataUploads") String datasetUploadsUri, + @JsonProperty("lastUpload") LastUpload lastUpload) { + this.meta = meta; + this.datasetUploadsUri = datasetUploadsUri; + this.lastUpload = lastUpload; + } + + /** + * @return dataset identifier + */ + public String getDatasetId() { + return meta.getIdentifier(); + } + + /** + * @return URI for all uploads of this dataset + */ + public String getUploadsUri() { + return datasetUploadsUri; + } + + /** + * @return {@link Upload} uri of the last upload + */ + public String getLastUploadUri() { + return lastUpload.uri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + + /** + * Wrapper class for the last upload. + * Only for deserialization. For internal use only. + */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonTypeName("dataUploadShort") + @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) + private static class LastUpload { + + private final String uri; + + private LastUpload(@JsonProperty("uri") String uri) { + this.uri = uri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/Execution.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/Execution.java new file mode 100644 index 000000000..807f94382 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/Execution.java @@ -0,0 +1,55 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.afm.Afm; +import com.gooddata.sdk.model.executeafm.resultspec.ResultSpec; + +/** + * Represents structure for triggering execution of contained AFM (Attributes Filters Metrics). + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonTypeName("execution") +public class Execution { + + private final Afm afm; + private ResultSpec resultSpec; + + @JsonCreator + public Execution(@JsonProperty("afm") final Afm afm, + @JsonProperty("resultSpec") final ResultSpec resultSpec) { + this.afm = afm; + this.resultSpec = resultSpec; + } + + public Execution(final Afm afm) { + this.afm = afm; + } + + public Afm getAfm() { + return afm; + } + + public ResultSpec getResultSpec() { + return resultSpec; + } + + public void setResultSpec(final ResultSpec resultSpec) { + this.resultSpec = resultSpec; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/IdentifierObjQualifier.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/IdentifierObjQualifier.java new file mode 100644 index 000000000..52305268d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/IdentifierObjQualifier.java @@ -0,0 +1,51 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm; + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Obj; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Qualifies metadata {@link Obj} using an identifier + */ +@JsonRootName("identifier") +public final class IdentifierObjQualifier implements ObjQualifier, Serializable { + + private static final long serialVersionUID = 4398691769334257408L; + private final String identifier; + + public IdentifierObjQualifier(final String identifier) { + this.identifier = identifier; + } + + @JsonValue + public String getIdentifier() { + return identifier; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IdentifierObjQualifier that = (IdentifierObjQualifier) o; + return Objects.equals(identifier, that.identifier); + } + + @Override + public int hashCode() { + return Objects.hash(identifier); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/LocalIdentifierQualifier.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/LocalIdentifierQualifier.java new file mode 100644 index 000000000..5a49c4dbc --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/LocalIdentifierQualifier.java @@ -0,0 +1,61 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm; + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Objects; + +import static org.apache.commons.lang3.Validate.notNull; + +/** + * Qualifies AFM object using an local identifier + */ +@JsonRootName("localIdentifier") +public final class LocalIdentifierQualifier implements Qualifier, Serializable { + + private static final long serialVersionUID = -7856385638703759024L; + + private final String localIdentifier; + + /** + * Creates a new instance of {@link LocalIdentifierQualifier}. + * + * @param localIdentifier The local identifier value. + */ + public LocalIdentifierQualifier(final String localIdentifier) { + this.localIdentifier = notNull(localIdentifier, "localIdentifier"); + } + + /** + * @return local identifier value + */ + @JsonValue + public String getLocalIdentifier() { + return localIdentifier; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final LocalIdentifierQualifier that = (LocalIdentifierQualifier) o; + return Objects.equals(localIdentifier, that.localIdentifier); + } + + @Override + public int hashCode() { + return Objects.hash(localIdentifier); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/ObjQualifier.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/ObjQualifier.java new file mode 100644 index 000000000..8f125fab9 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/ObjQualifier.java @@ -0,0 +1,30 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.gooddata.sdk.model.md.Obj; + +/** + * Qualifies metadata {@link Obj} + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = UriObjQualifier.class, name = "uri"), + @JsonSubTypes.Type(value = IdentifierObjQualifier.class, name = "identifier") +}) +public interface ObjQualifier extends Qualifier { + + /** + * Returns the qualifier in the form of uri. Default implementation throws {@link UnsupportedOperationException} + * + * @return uri qualifier + */ + default String getUri() { + throw new UnsupportedOperationException("This qualifier has no URI"); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/Qualifier.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/Qualifier.java new file mode 100644 index 000000000..dda15d711 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/Qualifier.java @@ -0,0 +1,22 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.gooddata.sdk.model.md.Obj; + +/** + * Qualifies metadata {@link Obj} or local AFM object + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ObjQualifier.class), + @JsonSubTypes.Type(value = LocalIdentifierQualifier.class, name = "localIdentifier") +}) +public interface Qualifier { + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/ResultPage.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/ResultPage.java new file mode 100644 index 000000000..1839ca11b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/ResultPage.java @@ -0,0 +1,56 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm; + +import com.gooddata.sdk.model.executeafm.response.ExecutionResponse; +import com.gooddata.sdk.model.executeafm.result.ExecutionResult; + +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static java.util.stream.Collectors.joining; + +/** + * Represents page of {@link ExecutionResult} to be requested, using + * {@link com.gooddata.sdk.executeafm.ExecuteAfmService#getResult(ExecutionResponse, ResultPage)} + */ +public class ResultPage { + + private final List offsets; + private final List limits; + + /** + * Creates new instance + * + * @param offsets list of page offsets + * @param limits list of page limits + */ + public ResultPage(final List offsets, final List limits) { + this.offsets = notEmpty(offsets, "offsets"); + this.limits = notEmpty(limits, "limits"); + if (offsets.size() != limits.size()) { + throw new IllegalArgumentException("Offsets and limits can't have different size."); + } + } + + private static String toQueryParam(final List list) { + return list.stream().map(String::valueOf).collect(joining("%2C")); + } + + /** + * @return page offsets joined and URL-encoded to be used as query parameter + */ + public String getOffsetsQueryParam() { + return toQueryParam(offsets); + } + + /** + * @return page limits joined and URL-encoded to be used as query parameter + */ + public String getLimitsQueryParam() { + return toQueryParam(limits); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/UriObjQualifier.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/UriObjQualifier.java new file mode 100644 index 000000000..55944043e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/UriObjQualifier.java @@ -0,0 +1,53 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm; + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Obj; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Qualifies metadata {@link Obj} using an URI + */ +@JsonRootName("uri") +public final class UriObjQualifier implements ObjQualifier, Serializable { + + private static final long serialVersionUID = 5505403156762360659L; + private final String uri; + + public UriObjQualifier(final String uri) { + this.uri = uri; + } + + @JsonValue + @Override + public String getUri() { + return uri; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof UriObjQualifier)) return false; + + final UriObjQualifier that = (UriObjQualifier) o; + return Objects.equals(uri, that.uri); + } + + @Override + public int hashCode() { + return Objects.hash(uri); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/VisualizationExecution.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/VisualizationExecution.java new file mode 100644 index 000000000..9ab1be7d6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/VisualizationExecution.java @@ -0,0 +1,95 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.executeafm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.model.executeafm.afm.filter.CompatibilityFilter; +import com.gooddata.sdk.model.executeafm.resultspec.ResultSpec; + +import java.util.List; + +/** + * Represents structure for triggering execution with reference to visualization object. + * Contains additional filters which should be merged with original ones defined in viz. object. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonTypeName("visualizationExecution") +public class VisualizationExecution { + + private final String reference; + private List filters; + private ResultSpec resultSpec; + + /** + * Constructor. + * Creates execution of visualization object without any additional filters or result specification. + * + * @param reference reference uri to visualization object metadata + */ + public VisualizationExecution(final String reference) { + this(reference, null, null); + } + + /** + * @param reference reference uri to visualization object metadata + * @param filters additional filters which should be merged + * @param resultSpec result specification of executed viz. object + */ + @JsonCreator + VisualizationExecution(@JsonProperty("reference") final String reference, + @JsonProperty("filters") final List filters, + @JsonProperty("resultSpec") final ResultSpec resultSpec) { + this.reference = reference; + this.resultSpec = resultSpec; + this.filters = filters; + } + + /** + * @return reference uri to visualization object metadata + */ + public String getReference() { + return reference; + } + + public List getFilters() { + return filters; + } + + /** + * Sets additional filters to this execution. + * + * @param filters additional filters + * @return updated execution + */ + public VisualizationExecution setFilters(final List filters) { + this.filters = filters; + return this; + } + + /** + * @return result specification of executed viz. object + */ + public ResultSpec getResultSpec() { + return resultSpec; + } + + /** + * Sets the result specification and returns this instance + * + * @param resultSpec result specification of executed viz. object + * @return updated execution + */ + public VisualizationExecution setResultSpec(final ResultSpec resultSpec) { + this.resultSpec = resultSpec; + return this; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/Afm.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/Afm.java new file mode 100644 index 000000000..4bd2bc35b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/Afm.java @@ -0,0 +1,145 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.afm.filter.CompatibilityFilter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; + +/** + * Attributes Filters and Measures in so called object form (could have MAQL form in future) + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Afm { + + private List attributes; + private List filters; + private List measures; + private List nativeTotals; + + @JsonCreator + public Afm(@JsonProperty("attributes") final List attributes, + @JsonProperty("filters") final List filters, + @JsonProperty("measures") final List measures, + @JsonProperty("nativeTotals") final List nativeTotals) { + this.attributes = attributes; + this.filters = filters; + this.measures = measures; + this.nativeTotals = nativeTotals; + } + + public Afm() { + } + + private static T getIdentifiable(final List toSearch, final String localIdentifier) { + return Optional.ofNullable(toSearch) + .flatMap(a -> a.stream().filter(i -> Objects.equals(localIdentifier, i.getLocalIdentifier())).findFirst()) + .orElseThrow(() -> new IllegalArgumentException(format("Item of localIdentifier=%s not found", localIdentifier))); + } + + /** + * Find {@link AttributeItem} within attributes by given localIdentifier + * + * @param localIdentifier identifier used for search + * @return found attribute or throws exception + */ + @JsonIgnore + public AttributeItem getAttribute(final String localIdentifier) { + return getIdentifiable(attributes, localIdentifier); + } + + /** + * Find {@link MeasureItem} within measures by given localIdentifier + * + * @param localIdentifier identifier used for search + * @return found measure or throws exception + */ + @JsonIgnore + public MeasureItem getMeasure(final String localIdentifier) { + return getIdentifiable(measures, localIdentifier); + } + + public List getAttributes() { + return attributes; + } + + public void setAttributes(final List attributes) { + this.attributes = attributes; + } + + public Afm addAttribute(final AttributeItem attribute) { + if (attributes == null) { + setAttributes(new ArrayList<>()); + } + attributes.add(notNull(attribute, "attribute")); + return this; + } + + public List getFilters() { + return filters; + } + + public void setFilters(final List filters) { + this.filters = filters; + } + + public Afm addFilter(final CompatibilityFilter filter) { + if (filters == null) { + setFilters(new ArrayList<>()); + } + filters.add(notNull(filter, "filter")); + return this; + } + + public List getMeasures() { + return measures; + } + + public void setMeasures(final List measures) { + this.measures = measures; + } + + public Afm addMeasure(final MeasureItem measure) { + if (measures == null) { + setMeasures(new ArrayList<>()); + } + measures.add(notNull(measure, "measure")); + return this; + } + + public List getNativeTotals() { + return nativeTotals; + } + + public void setNativeTotals(final List nativeTotals) { + this.nativeTotals = nativeTotals; + } + + public Afm addNativeTotal(final NativeTotalItem total) { + if (nativeTotals == null) { + setNativeTotals(new ArrayList<>()); + } + nativeTotals.add(notNull(total, "total")); + return this; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/Aggregation.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/Aggregation.java new file mode 100644 index 000000000..e30c65bf0 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/Aggregation.java @@ -0,0 +1,22 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Represents possible aggregation values at {@link SimpleMeasureDefinition} + */ +public enum Aggregation { + + SUM, COUNT, AVG, MIN, MAX, MEDIAN, RUNSUM; + + @JsonValue + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ArithmeticMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ArithmeticMeasureDefinition.java new file mode 100644 index 000000000..77fa740c6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ArithmeticMeasureDefinition.java @@ -0,0 +1,90 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Arithmetic measure definition representing aggregation of existing measures, for example sum of measures, difference,... + */ +@JsonRootName(PreviousPeriodMeasureDefinition.NAME) +public class ArithmeticMeasureDefinition implements MeasureDefinition { + + static final String NAME = "arithmeticMeasure"; + private static final long serialVersionUID = -2597112924341600780L; + private final List measureIdentifiers; + private final String operator; + + /** + * Constructor of {@link ArithmeticMeasureDefinition} + * + * @param measureIdentifiers local identifiers of measures + * @param operator operator used for aggregation, for example sum, difference, multiplication, ratio, growth + */ + @JsonCreator + public ArithmeticMeasureDefinition( + @JsonProperty("measureIdentifiers") final List measureIdentifiers, + @JsonProperty("operator") final String operator) { + this.measureIdentifiers = measureIdentifiers; + this.operator = operator; + } + + /** + * no qualifiers are used, only local identifiers are used see {@link ArithmeticMeasureDefinition#getOperator()} + * + * @return empty set + */ + @Override + public Collection getObjQualifiers() { + return Collections.EMPTY_SET; //has no qualifiers + } + + /** + * no conversion is done, because this definition uses only local identifiers + * + * @return this instance + */ + @Override + public MeasureDefinition withObjUriQualifiers(ObjQualifierConverter objQualifierConverter) { + return this; //nothing to convert + } + + @Override + public boolean isAdHoc() { + return true; + } + + /** + * get local identifiers of used measures + * + * @return local identifiers of measure + */ + public List getMeasureIdentifiers() { + return measureIdentifiers; + } + + /** + * get used operator + * + * @return used operator + */ + public String getOperator() { + return operator; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/AttributeItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/AttributeItem.java new file mode 100644 index 000000000..8f49bb864 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/AttributeItem.java @@ -0,0 +1,104 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.md.AttributeDisplayForm; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Represents attribute within {@link Afm} + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AttributeItem implements LocallyIdentifiable, Serializable { + + private static final long serialVersionUID = -1484150046473673413L; + private final String localIdentifier; + private final ObjQualifier displayForm; + + private String alias; + + /** + * Creates new instance + * + * @param displayForm qualifier of {@link AttributeDisplayForm} representing the attribute + * @param localIdentifier local identifier, unique within {@link Afm} + * @param alias attribute alias + */ + @JsonCreator + public AttributeItem(@JsonProperty("displayForm") final ObjQualifier displayForm, + @JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("alias") final String alias) { + this.localIdentifier = localIdentifier; + this.displayForm = displayForm; + this.alias = alias; + } + + /** + * Creates new instance + * + * @param displayForm qualifier of {@link AttributeDisplayForm} representing the attribute + * @param localIdentifier local identifier, unique within {@link Afm} + */ + public AttributeItem(final ObjQualifier displayForm, final String localIdentifier) { + this.displayForm = displayForm; + this.localIdentifier = localIdentifier; + } + + @Override + public String getLocalIdentifier() { + return localIdentifier; + } + + /** + * @return qualifier of {@link AttributeDisplayForm} representing the attribute + */ + public ObjQualifier getDisplayForm() { + return displayForm; + } + + /** + * @return attribute alias (used as header in result) + */ + public String getAlias() { + return alias; + } + + /** + * Sets attribute alias (used as header in result) + * + * @param alias alias + */ + public void setAlias(final String alias) { + this.alias = alias; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AttributeItem that = (AttributeItem) o; + return Objects.equals(localIdentifier, that.localIdentifier) && + Objects.equals(displayForm, that.displayForm); + } + + @Override + public int hashCode() { + return Objects.hash(localIdentifier, displayForm); + } +} + diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/DerivedMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/DerivedMeasureDefinition.java new file mode 100644 index 000000000..365b111da --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/DerivedMeasureDefinition.java @@ -0,0 +1,64 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * The superclass of the {@link MeasureDefinition} classes that are derived from the master measure and have the identifier of the master measure. + */ +@SuppressWarnings("deprecation") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = PopMeasureDefinition.class, name = PopMeasureDefinition.NAME), + @JsonSubTypes.Type(value = OverPeriodMeasureDefinition.class, name = OverPeriodMeasureDefinition.NAME), + @JsonSubTypes.Type(value = PreviousPeriodMeasureDefinition.class, name = PreviousPeriodMeasureDefinition.NAME) +}) +public abstract class DerivedMeasureDefinition implements MeasureDefinition { + + private static final long serialVersionUID = -1203802872091017113L; + + protected final String measureIdentifier; + + /** + * Create a new instance of {@link DerivedMeasureDefinition}. + * + * @param measureIdentifier The local identifier of the master measure this derived measure refers to. The parameter must not be null. + * @throws IllegalArgumentException Thrown when required parameter is null. + */ + DerivedMeasureDefinition(final String measureIdentifier) { + this.measureIdentifier = notNull(measureIdentifier, "measureIdentifier"); + } + + /** + * The local identifier of the master measure this derived measure refers to. + * + * @return The local identifier of the master measure. + */ + @JsonProperty + public String getMeasureIdentifier() { + return measureIdentifier; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final DerivedMeasureDefinition that = (DerivedMeasureDefinition) o; + return Objects.equals(measureIdentifier, that.measureIdentifier); + } + + @Override + public int hashCode() { + return Objects.hash(measureIdentifier); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/LocallyIdentifiable.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/LocallyIdentifiable.java new file mode 100644 index 000000000..5c556d91c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/LocallyIdentifiable.java @@ -0,0 +1,16 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +/** + * Marker interface for all locally identifiable objects having local identifier in AFM + */ +public interface LocallyIdentifiable { + /** + * @return value of local identifier, unique within {@link Afm} + */ + String getLocalIdentifier(); +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/MeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/MeasureDefinition.java new file mode 100644 index 000000000..e48df6116 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/MeasureDefinition.java @@ -0,0 +1,76 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.gooddata.sdk.model.executeafm.IdentifierObjQualifier; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.md.Metric; +import com.gooddata.sdk.model.md.visualization.VOPopMeasureDefinition; +import com.gooddata.sdk.model.md.visualization.VOSimpleMeasureDefinition; + +import java.io.Serializable; +import java.util.Collection; + +@SuppressWarnings("deprecation") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = SimpleMeasureDefinition.class, name = SimpleMeasureDefinition.NAME), + @JsonSubTypes.Type(value = PopMeasureDefinition.class, name = PopMeasureDefinition.NAME), + @JsonSubTypes.Type(value = VOSimpleMeasureDefinition.class, name = VOSimpleMeasureDefinition.NAME), + @JsonSubTypes.Type(value = VOPopMeasureDefinition.class, name = VOPopMeasureDefinition.NAME), + @JsonSubTypes.Type(value = OverPeriodMeasureDefinition.class, name = OverPeriodMeasureDefinition.NAME), + @JsonSubTypes.Type(value = PreviousPeriodMeasureDefinition.class, name = PreviousPeriodMeasureDefinition.NAME), + @JsonSubTypes.Type(value = ArithmeticMeasureDefinition.class, name = ArithmeticMeasureDefinition.NAME) +}) +public interface MeasureDefinition extends Serializable { + + /** + * Returns the definition in the form of uri of {@link Metric}. + * Default implementation throws {@link UnsupportedOperationException} + * + * @return uri of the measure + */ + @JsonIgnore + default String getUri() { + throw new UnsupportedOperationException("This definition has no URI"); + } + + /** + * Returns all the qualifiers used by the measure definition and its encapsulated objects. + *

+ * This information comes handy if it is necessary, for example, to convert the measure definition to use just the URI object qualifiers instead of the + * identifier object qualifiers. It can be used to gather these for a conversion service. + * + * @return all the qualifiers the measure definition uses, even in its encapsulated objects (apart from the measure filters) + */ + @JsonIgnore + Collection getObjQualifiers(); + + /** + * Copy itself using the given object qualifier converter in case when {@link IdentifierObjQualifier} instances are used in the object otherwise the + * original object is returned. + *

+ * The provided converter must be able to handle the conversion for the qualifiers that are of the {@link IdentifierObjQualifier} type that are used by + * this object or its encapsulated child objects. + * + * @param objQualifierConverter The function that converts identifier qualifiers to the matching URI qualifiers. In case when the object uses the identifier qualifiers, it + * will return a new copy of itself or its encapsulated objects that used URI qualifiers, otherwise the original object is returned. + * The parameter must not be null. + * @return copy of itself with replaced qualifiers in case when some {@link IdentifierObjQualifier} were used, otherwise original object is returned. + * @throws IllegalArgumentException The exception is thrown when conversion for the identifier qualifier used by this measure definition could not be made by the provided + * converter or when provided converter is null. + */ + MeasureDefinition withObjUriQualifiers(ObjQualifierConverter objQualifierConverter); + + /** + * @return true if this definition represents ad hoc specified measure, false otherwise + */ + @JsonIgnore + boolean isAdHoc(); +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/MeasureItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/MeasureItem.java new file mode 100644 index 000000000..a22d2c914 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/MeasureItem.java @@ -0,0 +1,115 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Represents measure within {@link Afm} + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MeasureItem implements LocallyIdentifiable, Serializable { + + private static final long serialVersionUID = -8641866340075567437L; + private final MeasureDefinition definition; + private final String localIdentifier; + private String alias; + private String format; + + public MeasureItem(final MeasureDefinition definition, final String localIdentifier) { + this.definition = definition; + this.localIdentifier = localIdentifier; + } + + @JsonCreator + public MeasureItem(@JsonProperty("definition") final MeasureDefinition definition, + @JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("alias") final String alias, + @JsonProperty("format") final String format) { + this.definition = definition; + this.localIdentifier = localIdentifier; + this.alias = alias; + this.format = format; + } + + /** + * @return contained definition of measure + */ + public MeasureDefinition getDefinition() { + return definition; + } + + @Override + public String getLocalIdentifier() { + return localIdentifier; + } + + /** + * @return specified measure alias (will be used as header in result) + */ + public String getAlias() { + return alias; + } + + /** + * Sets measure alias (will be used as header in result) + * + * @param alias alias + */ + public void setAlias(final String alias) { + this.alias = alias; + } + + /** + * @return measure format (used to format measure values in result) + */ + public String getFormat() { + return format; + } + + /** + * Sets measure format (used to format measure values in result) + * + * @param format + */ + public void setFormat(final String format) { + this.format = format; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * @return true whether contains definition of measure not stored in metadata (= defined ad-hoc), false otherwise + */ + @JsonIgnore + public boolean isAdHoc() { + return definition.isAdHoc(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MeasureItem that = (MeasureItem) o; + return Objects.equals(definition, that.definition) && + Objects.equals(localIdentifier, that.localIdentifier); + } + + @Override + public int hashCode() { + return Objects.hash(definition, localIdentifier); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/NativeTotalItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/NativeTotalItem.java new file mode 100644 index 000000000..fdb5af35b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/NativeTotalItem.java @@ -0,0 +1,60 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collections; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Native total definition + */ +public class NativeTotalItem { + private final String measureIdentifier; + private final List attributeIdentifiers; + + /** + * Native total definition + * + * @param measureIdentifier measure on which is total defined + * @param attributeIdentifiers subset of internal attribute identifiers in AFM defining total placement + */ + @JsonCreator + public NativeTotalItem( + @JsonProperty("measureIdentifier") final String measureIdentifier, + @JsonProperty("attributeIdentifiers") final List attributeIdentifiers) { + this.measureIdentifier = notEmpty(measureIdentifier, "measureIdentifier"); + this.attributeIdentifiers = attributeIdentifiers == null ? Collections.emptyList() : attributeIdentifiers; + } + + /** + * internal identifier of measure in AFM, on which is total defined + * + * @return measure + */ + public String getMeasureIdentifier() { + return measureIdentifier; + } + + /** + * subset of internal attribute identifiers in AFM defining total placement + * + * @return list of identifiers (never null) + */ + public List getAttributeIdentifiers() { + return attributeIdentifiers; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ObjIdentifierUtilities.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ObjIdentifierUtilities.java new file mode 100644 index 000000000..035a5243c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ObjIdentifierUtilities.java @@ -0,0 +1,67 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.gooddata.sdk.model.executeafm.IdentifierObjQualifier; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; + +import java.util.function.Function; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; + +/** + * The utilities for conversion of the objects that use {@link IdentifierObjQualifier} to objects that use {@link UriObjQualifier}. + */ +abstract class ObjIdentifierUtilities { + + /** + * Copy {@code objectToBeCopied} via provided {@code objectCopyFactory} in case when {@code qualifierForPossibleConversion} is of {@link + * IdentifierObjQualifier} type. Otherwise the original object is returned. + * + * @param objectToBeCopied The object that should be copied in case {@code qualifierForPossibleConversion} is of {@link IdentifierObjQualifier} type. The parameter must + * not be null. + * @param qualifierForPossibleConversion The qualifier that defines if {@code objectToBeCopied} will be copied or not. In case when it is of the {@link IdentifierObjQualifier} type, + * it will be converted via {@code qualifierConverter} and used in copy of the {@code objectToBeCopied}. The parameter must not be null. + * @param objectCopyFactory The factory method that accepts the result of the {@code qualifierForPossibleConversion} conversion in form of {@link UriObjQualifier} and + * returns new copy of the {@code objectToBeCopied}. The parameter must not be null. + * @param qualifierConverter The convert that can convert {@code qualifierForPossibleConversion} into its matching {@link UriObjQualifier} form. The parameter must not be + * null. + * @param The type of the object that should be copied. + * @return Copy of the {@code objectToBeCopied} in case when {@code qualifierForPossibleConversion} was of {@link IdentifierObjQualifier} type. Otherwise + * the original object is returned. + * @throws IllegalArgumentException The exception is thrown when required parameter is null. + */ + static R copyIfNecessary(final R objectToBeCopied, + final ObjQualifier qualifierForPossibleConversion, + final Function objectCopyFactory, + final ObjQualifierConverter qualifierConverter) { + notNull(objectToBeCopied, "objectToBeCopied"); + notNull(qualifierForPossibleConversion, "qualifierForPossibleConversion"); + notNull(objectCopyFactory, "objectCopyFactory"); + notNull(qualifierConverter, "qualifierConverter"); + + if (qualifierForPossibleConversion instanceof IdentifierObjQualifier) { + final IdentifierObjQualifier identifierQualifierToConvert = (IdentifierObjQualifier) qualifierForPossibleConversion; + return copyWithUriQualifier(identifierQualifierToConvert, objectCopyFactory, qualifierConverter); + } + return objectToBeCopied; + } + + private static R copyWithUriQualifier(final IdentifierObjQualifier identifierQualifierToConvert, + final Function objectCopyFactory, + final ObjQualifierConverter qualifierConverter) { + return qualifierConverter.convertToUriQualifier(identifierQualifierToConvert) + .map(objectCopyFactory) + .orElseThrow(() -> buildExceptionForFailedConversion(identifierQualifierToConvert)); + } + + private static IllegalArgumentException buildExceptionForFailedConversion(final IdentifierObjQualifier qualifierFailedToConvert) { + return new IllegalArgumentException(format("Supplied converter does not provide conversion for '%s'!", qualifierFailedToConvert)); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ObjQualifierConverter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ObjQualifierConverter.java new file mode 100644 index 000000000..4196d64fa --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/ObjQualifierConverter.java @@ -0,0 +1,26 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.gooddata.sdk.model.executeafm.IdentifierObjQualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; + +import java.util.Optional; + +/** + * The interface of the function that converts {@link IdentifierObjQualifier} to the matching {@link UriObjQualifier}. + */ +@FunctionalInterface +public interface ObjQualifierConverter { + + /** + * Convert provided {@link IdentifierObjQualifier} to the matching {@link UriObjQualifier}. + * + * @param identifierObjQualifier The identifier that must be converted. + * @return The optional matching {@link UriObjQualifier} obtained by the conversion. + */ + Optional convertToUriQualifier(IdentifierObjQualifier identifierObjQualifier); +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/OverPeriodDateAttribute.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/OverPeriodDateAttribute.java new file mode 100644 index 000000000..4c45443fa --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/OverPeriodDateAttribute.java @@ -0,0 +1,82 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; + +import java.io.Serializable; +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Definition of the {@link OverPeriodMeasureDefinition} attribute. + */ +public class OverPeriodDateAttribute implements Serializable { + + private static final long serialVersionUID = 2311364644023464059L; + + private final ObjQualifier attribute; + private final Integer periodsAgo; + + /** + * Create a new instance of {@link OverPeriodDateAttribute}. + * + * @param attribute The {@link ObjQualifier} of the attribute from the date data set that defines the PoP period and date data set. The parameter must not be + * null. + * @param periodsAgo The number of periods defined by the {@code attribute} about which this period will be shifted about. The positive number shifts the period to + * the past, the negative to the future. The parameter must not be null. + * @throws IllegalArgumentException Thrown when one of the required parameter is null. + */ + @JsonCreator + public OverPeriodDateAttribute( + @JsonProperty("attribute") final ObjQualifier attribute, + @JsonProperty("periodsAgo") final Integer periodsAgo) { + this.attribute = notNull(attribute, "attribute"); + this.periodsAgo = notNull(periodsAgo, "periodsAgo"); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final OverPeriodDateAttribute that = (OverPeriodDateAttribute) o; + return Objects.equals(attribute, that.attribute) && + Objects.equals(periodsAgo, that.periodsAgo); + } + + @Override + public int hashCode() { + return Objects.hash(attribute, periodsAgo); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * The {@link ObjQualifier} of the attribute from the date data set that defines the PoP period. + * + * @return The date data set attribute that defines the PoP attribute. + */ + public ObjQualifier getAttribute() { + return attribute; + } + + /** + * The number of periods defined by the {@code attribute} about which this period will be shifted about. The positive number shifts the period to + * the past, the negative to the future. + * + * @return The number of periods the data will be shifted about. + */ + public Integer getPeriodsAgo() { + return periodsAgo; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/OverPeriodMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/OverPeriodMeasureDefinition.java new file mode 100644 index 000000000..0b29a36b3 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/OverPeriodMeasureDefinition.java @@ -0,0 +1,113 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.model.executeafm.afm.OverPeriodMeasureDefinition.NAME; + +/** + * Definition of the period over period measure that is used for the Same period last year and Same period 2 years back comparisons. + */ +@JsonRootName(NAME) +public class OverPeriodMeasureDefinition extends DerivedMeasureDefinition { + + static final String NAME = "overPeriodMeasure"; + private static final long serialVersionUID = -8904516814279504098L; + private final List dateAttributes; + + /** + * Create a new instance of {@link OverPeriodMeasureDefinition}. + * + * @param measureIdentifier The local identifier of the measure this PoP measure refers to. The parameter must not be null. + * @param dateAttributes The date attributes that defines how this measure will be shifted in time. The parameter must not be null. + * @throws IllegalArgumentException Thrown when {@code dateAttributes} list is empty or required parameter is null. + */ + @JsonCreator + public OverPeriodMeasureDefinition( + @JsonProperty("measureIdentifier") final String measureIdentifier, + @JsonProperty("dateAttributes") final List dateAttributes) { + super(measureIdentifier); + this.dateAttributes = notEmpty(dateAttributes, "dateAttributes"); + } + + @Override + public Collection getObjQualifiers() { + return this.dateAttributes.stream() + .map(OverPeriodDateAttribute::getAttribute) + .collect(Collectors.toSet()); + } + + @Override + public MeasureDefinition withObjUriQualifiers(final ObjQualifierConverter objQualifierConverter) { + notNull(objQualifierConverter, "objQualifierConverter"); + return new OverPeriodMeasureDefinition(measureIdentifier, copyAttributesWithUriQualifiers(objQualifierConverter)); + } + + private List copyAttributesWithUriQualifiers(final ObjQualifierConverter objQualifierConverter) { + return dateAttributes.stream() + .map(attribute -> copyWithUriQualifier(attribute, objQualifierConverter)) + .collect(Collectors.toList()); + } + + private OverPeriodDateAttribute copyWithUriQualifier(final OverPeriodDateAttribute attribute, final ObjQualifierConverter objQualifierConverter) { + return ObjIdentifierUtilities.copyIfNecessary( + attribute, + attribute.getAttribute(), + uriObjQualifier -> new OverPeriodDateAttribute(uriObjQualifier, attribute.getPeriodsAgo()), + objQualifierConverter + ); + } + + /** + * Determine if measure is ad-hoc, i.e., if it does not exist in the catalog and was created on fly. + * + * @return always true ({@link OverPeriodMeasureDefinition} is always ad-hoc) + */ + @Override + public boolean isAdHoc() { + return true; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + final OverPeriodMeasureDefinition that = (OverPeriodMeasureDefinition) o; + return Objects.equals(dateAttributes, that.dateAttributes); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), dateAttributes); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * The date attributes that defines how this measure will be shifted in time. + * + * @return The list of date attributes. + */ + public List getDateAttributes() { + return dateAttributes; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PopMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PopMeasureDefinition.java new file mode 100644 index 000000000..966618f1f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PopMeasureDefinition.java @@ -0,0 +1,99 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +import static com.gooddata.sdk.model.executeafm.afm.PopMeasureDefinition.NAME; + +/** + * Definition of so called "period over period" measure. + * + * @deprecated Use {@link OverPeriodMeasureDefinition} with {@link OverPeriodDateAttribute#getPeriodsAgo()} set to {@code 1} instead. + * Let's remove it once it's removed from API. + */ +@Deprecated +@JsonRootName(NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class PopMeasureDefinition extends DerivedMeasureDefinition { + + static final String NAME = "popMeasure"; + private static final long serialVersionUID = 1430640153994197345L; + private final ObjQualifier popAttribute; + + /** + * Creates new definition from given measure identifier referencing another measure in {@link Afm} and given attribute qualifier (should qualify date + * attribute) + * + * @param measureIdentifier measure identifier + * @param popAttribute "period over period" date attribute + */ + @JsonCreator + public PopMeasureDefinition(@JsonProperty("measureIdentifier") final String measureIdentifier, + @JsonProperty("popAttribute") final ObjQualifier popAttribute) { + super(measureIdentifier); + this.popAttribute = popAttribute; + } + + @Override + public MeasureDefinition withObjUriQualifiers(final ObjQualifierConverter objQualifierConverter) { + return ObjIdentifierUtilities.copyIfNecessary( + this, + popAttribute, + uriObjQualifier -> new PopMeasureDefinition(measureIdentifier, uriObjQualifier), + objQualifierConverter + ); + } + + /** + * Determine if measure is ad-hoc + * + * @return always true (PopMeasure is always ad-hoc) + */ + @Override + public boolean isAdHoc() { + return true; + } + + public ObjQualifier getPopAttribute() { + return popAttribute; + } + + @Override + public Collection getObjQualifiers() { + return popAttribute == null + ? Collections.emptySet() + : Collections.singleton(popAttribute); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + final PopMeasureDefinition that = (PopMeasureDefinition) o; + return Objects.equals(popAttribute, that.popAttribute); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), popAttribute); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PreviousPeriodDateDataSet.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PreviousPeriodDateDataSet.java new file mode 100644 index 000000000..c00744557 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PreviousPeriodDateDataSet.java @@ -0,0 +1,82 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.afm.filter.DateFilter; + +import java.io.Serializable; +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Definition of the {@link PreviousPeriodMeasureDefinition} data set. + */ +public class PreviousPeriodDateDataSet implements Serializable { + + private static final long serialVersionUID = 2311364644023464059L; + + private final ObjQualifier dataSet; + private final Integer periodsAgo; + + /** + * Create a new instance of {@link PreviousPeriodDateDataSet}. + * + * @param dataSet The {@link ObjQualifier} of the data set that match one of the {@link DateFilter} in the execution. The parameter must not be null. + * @param periodsAgo The number of periods defined by the matching date filter which this period will be shifted about. The positive number shifts the period to + * the past, the negative to the future. The parameter must not be null. + * @throws IllegalArgumentException Thrown when one of the required parameter is null. + */ + @JsonCreator + public PreviousPeriodDateDataSet( + @JsonProperty("dataSet") final ObjQualifier dataSet, + @JsonProperty("periodsAgo") final Integer periodsAgo) { + this.dataSet = notNull(dataSet, "dataSet"); + this.periodsAgo = notNull(periodsAgo, "periodsAgo"); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final PreviousPeriodDateDataSet that = (PreviousPeriodDateDataSet) o; + return Objects.equals(dataSet, that.dataSet) && + Objects.equals(periodsAgo, that.periodsAgo); + } + + @Override + public int hashCode() { + return Objects.hash(dataSet, periodsAgo); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * The {@link ObjQualifier} of the data set that match one of the {@link DateFilter} in the execution. + * + * @return The data set for matching of the AFM filter. + */ + public ObjQualifier getDataSet() { + return dataSet; + } + + /** + * The number of periods defined by the matching date filter which this period will be shifted about. The positive number shifts the period to + * the past, the negative to the future. + * + * @return The number of periods the data will be shifted about. + */ + public Integer getPeriodsAgo() { + return periodsAgo; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PreviousPeriodMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PreviousPeriodMeasureDefinition.java new file mode 100644 index 000000000..096cb8be1 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/PreviousPeriodMeasureDefinition.java @@ -0,0 +1,113 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.model.executeafm.afm.PreviousPeriodMeasureDefinition.NAME; + +/** + * Definition of the period over period measure that is used for the Previous period comparison. + */ +@JsonRootName(NAME) +public class PreviousPeriodMeasureDefinition extends DerivedMeasureDefinition { + + static final String NAME = "previousPeriodMeasure"; + private static final long serialVersionUID = -4741355657671354062L; + private final List dateDataSets; + + /** + * Create a new instance of {@link PreviousPeriodMeasureDefinition}. + * + * @param measureIdentifier The local identifier of the measure this PoP measure refers to. The parameter must not be null. + * @param dateDataSets The date data sets that defines how this measure will be shifted in time. The parameter must not be null. + * @throws IllegalArgumentException Thrown when {@code attributes} list is empty or required parameter is null. + */ + @JsonCreator + public PreviousPeriodMeasureDefinition( + @JsonProperty("measureIdentifier") final String measureIdentifier, + @JsonProperty("dateDataSets") final List dateDataSets) { + super(measureIdentifier); + this.dateDataSets = notEmpty(dateDataSets, "dateDataSets"); + } + + @Override + public Collection getObjQualifiers() { + return this.dateDataSets.stream() + .map(PreviousPeriodDateDataSet::getDataSet) + .collect(Collectors.toSet()); + } + + @Override + public MeasureDefinition withObjUriQualifiers(final ObjQualifierConverter objQualifierConverter) { + notNull(objQualifierConverter, "objQualifierConverter"); + return new PreviousPeriodMeasureDefinition(measureIdentifier, copyDataSetsWithUriQualifiers(objQualifierConverter)); + } + + private List copyDataSetsWithUriQualifiers(final ObjQualifierConverter objQualifierConverter) { + return dateDataSets.stream() + .map(dataSet -> copyWithUriQualifier(dataSet, objQualifierConverter)) + .collect(Collectors.toList()); + } + + private PreviousPeriodDateDataSet copyWithUriQualifier(final PreviousPeriodDateDataSet dataSet, final ObjQualifierConverter objQualifierConverter) { + return ObjIdentifierUtilities.copyIfNecessary( + dataSet, + dataSet.getDataSet(), + uriObjQualifier -> new PreviousPeriodDateDataSet(uriObjQualifier, dataSet.getPeriodsAgo()), + objQualifierConverter + ); + } + + /** + * Determine if measure is ad-hoc, i.e., if it does not exist in the catalog and was created on fly. + * + * @return always true ({@link PreviousPeriodMeasureDefinition} is always ad-hoc) + */ + @Override + public boolean isAdHoc() { + return true; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + final PreviousPeriodMeasureDefinition that = (PreviousPeriodMeasureDefinition) o; + return Objects.equals(dateDataSets, that.dateDataSets); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), dateDataSets); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * The date data sets that defines how this measure will be shifted in time. + * + * @return The list of date data sets. + */ + public List getDateDataSets() { + return dateDataSets; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/SimpleMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/SimpleMeasureDefinition.java new file mode 100644 index 000000000..23eefe764 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/SimpleMeasureDefinition.java @@ -0,0 +1,233 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.afm.filter.FilterItem; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.model.executeafm.afm.SimpleMeasureDefinition.NAME; +import static java.util.Arrays.asList; + +/** + * Definition of simple measure + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonRootName(NAME) +public class SimpleMeasureDefinition implements MeasureDefinition { + + static final String NAME = "measure"; + private static final long serialVersionUID = -385490772711914776L; + private final ObjQualifier item; + private String aggregation; + private Boolean computeRatio; + private List filters; + + public SimpleMeasureDefinition(final ObjQualifier item) { + this.item = item; + } + + /** + * Creates new definition + * + * @param item item which is measured, can be attribute, fact or another measure + * @param aggregation additional aggregation applied + * @param computeRatio whether should be shown as ratio + * @param filters additional filters applied + */ + @JsonCreator + public SimpleMeasureDefinition(@JsonProperty("item") final ObjQualifier item, + @JsonProperty("aggregation") final String aggregation, + @JsonProperty("computeRatio") final Boolean computeRatio, + @JsonProperty("filters") final List filters) { + this.item = item; + this.aggregation = aggregation; + this.computeRatio = computeRatio; + this.filters = filters; + } + + /** + * Creates new definition + * + * @param item item which is measured, can be attribute, fact or another measure + * @param aggregation additional aggregation applied + * @param computeRatio whether should be shown as ratio + * @param filters additional filters applied + */ + public SimpleMeasureDefinition(final ObjQualifier item, final Aggregation aggregation, final Boolean computeRatio, + final List filters) { + this(item, notNull(aggregation, "aggregation").toString(), computeRatio, filters); + } + + /** + * Creates new definition + * + * @param item item which is measured, can be attribute, fact or another measure + * @param aggregation additional aggregation applied + * @param computeRatio whether should be shown as ratio + * @param filters additional filters applied + */ + public SimpleMeasureDefinition(final ObjQualifier item, final Aggregation aggregation, final Boolean computeRatio, final FilterItem... filters) { + this(item, aggregation, computeRatio, asList(filters)); + } + + @Override + public MeasureDefinition withObjUriQualifiers(final ObjQualifierConverter objQualifierConverter) { + return ObjIdentifierUtilities.copyIfNecessary( + this, + item, + uriObjQualifier -> new SimpleMeasureDefinition(uriObjQualifier, aggregation, computeRatio, filters), + objQualifierConverter + ); + } + + @Override + public boolean isAdHoc() { + return hasAggregation() || hasComputeRatio() || hasFilters(); + } + + @Override + public String getUri() { + return getItem().getUri(); + } + + @Override + public Collection getObjQualifiers() { + return item == null + ? Collections.emptySet() + : Collections.singleton(item); + } + + /** + * @return measured item, can be attribute, fact or another measure + */ + public ObjQualifier getItem() { + return item; + } + + /** + * @return additional aggregation applied + */ + public String getAggregation() { + return aggregation; + } + + /** + * Set additional aggregation applied + * + * @param aggregation additional aggregation applied + */ + public void setAggregation(final String aggregation) { + this.aggregation = aggregation; + } + + /** + * Set additional aggregation applied + * + * @param aggregation additional aggregation applied + */ + public void setAggregation(final Aggregation aggregation) { + setAggregation(notNull(aggregation, "aggregation").toString()); + } + + /** + * @return true when should be shown as ratio, false otherwise + */ + public Boolean getComputeRatio() { + return computeRatio; + } + + /** + * Set whether should be shown as ratio + * + * @param computeRatio whether should be shown as ratio + */ + public void setComputeRatio(final Boolean computeRatio) { + this.computeRatio = computeRatio; + } + + /** + * @return additional filters applied + */ + public List getFilters() { + return filters; + } + + /** + * Set additional filters applied + * + * @param filters additional filters applied + */ + public void setFilters(final List filters) { + this.filters = filters; + } + + /** + * Apply additional filter + * + * @param filter filter to be applied + */ + public void addFilter(final FilterItem filter) { + if (filters == null) { + filters = new ArrayList<>(); + } + filters.add(notNull(filter, "filter")); + } + + /** + * @return true when filters are set, false otherwise + */ + public boolean hasFilters() { + return filters != null && !filters.isEmpty(); + } + + /** + * @return true when computeRatio is set, false otherwise + */ + public boolean hasComputeRatio() { + return computeRatio != null && computeRatio; + } + + /** + * @return true when additional aggregation is set, false otherwise + */ + public boolean hasAggregation() { + return aggregation != null; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final SimpleMeasureDefinition that = (SimpleMeasureDefinition) o; + return Objects.equals(item, that.item) && + Objects.equals(aggregation, that.aggregation) && + Objects.equals(computeRatio, that.computeRatio) && + Objects.equals(filters, that.filters); + } + + @Override + public int hashCode() { + return Objects.hash(item, aggregation, computeRatio, filters); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} + diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AbsoluteDateFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AbsoluteDateFilter.java new file mode 100644 index 000000000..2e6d87022 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AbsoluteDateFilter.java @@ -0,0 +1,91 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GDLocalDate; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; + +import java.time.LocalDate; +import java.util.Objects; + +/** + * Represents {@link DateFilter} specifying exact from and to dates. + */ +@JsonRootName(AbsoluteDateFilter.NAME) +public class AbsoluteDateFilter extends DateFilter { + + static final String NAME = "absoluteDateFilter"; + private static final long serialVersionUID = -1857726227400504182L; + @GDLocalDate + private final LocalDate from; + @GDLocalDate + private final LocalDate to; + + /** + * Creates new filter instance + * + * @param dataSet qualifier of date dimension dataset + * @param from date from + * @param to date to + */ + @JsonCreator + public AbsoluteDateFilter(@JsonProperty("dataSet") final ObjQualifier dataSet, + @JsonProperty("from") final LocalDate from, + @JsonProperty("to") final LocalDate to) { + super(dataSet); + this.from = from; + this.to = to; + } + + /** + * @return date from + */ + public LocalDate getFrom() { + return from; + } + + /** + * @return date to + */ + public LocalDate getTo() { + return to; + } + + @Override + public FilterItem withObjUriQualifier(final UriObjQualifier qualifier) { + return new AbsoluteDateFilter(qualifier, from, to); + } + + @Override + @JsonIgnore + public boolean isAllTimeSelected() { + return getFrom() == null || getTo() == null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AbsoluteDateFilter that = (AbsoluteDateFilter) o; + return super.equals(that) && Objects.equals(from, that.from) && Objects.equals(to, that.to); + } + + @Override + public int hashCode() { + return Objects.hash(from, to, super.hashCode()); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AttributeFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AttributeFilter.java new file mode 100644 index 000000000..8edd5ff31 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AttributeFilter.java @@ -0,0 +1,57 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.gooddata.sdk.model.executeafm.ObjQualifier; + +import java.io.Serializable; +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Represents filter by attribute. + */ +public abstract class AttributeFilter implements FilterItem, Serializable { + + private static final long serialVersionUID = 1125099080537899747L; + private final ObjQualifier displayForm; + + /** + * Creates new filter + * + * @param displayForm qualifier of attribute's display form to be filtered + */ + AttributeFilter(final ObjQualifier displayForm) { + this.displayForm = notNull(displayForm, "displayForm"); + } + + /** + * @return filtered attribute's display form qualifier + */ + public ObjQualifier getDisplayForm() { + return displayForm; + } + + @Override + public ObjQualifier getObjQualifier() { + return getDisplayForm(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AttributeFilter that = (AttributeFilter) o; + return Objects.equals(displayForm, that.displayForm); + } + + @Override + public int hashCode() { + return Objects.hash(displayForm); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AttributeFilterElements.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AttributeFilterElements.java new file mode 100644 index 000000000..1f01f6e81 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/AttributeFilterElements.java @@ -0,0 +1,88 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static com.fasterxml.jackson.databind.JsonMappingException.from; + +/** + * Represents elements, which can be used as value of "in" or "notIn" by {@link AttributeFilter}. + */ +@JsonSerialize(using = AttributeFilterElements.Serializer.class) +@JsonDeserialize(using = AttributeFilterElements.Deserializer.class) +public interface AttributeFilterElements { + + /** + * Elements the filter refers to. + * + * @return filter elements. + */ + List getElements(); + + class Serializer extends JsonSerializer { + + private static void serializeWrapped(String name, AttributeFilterElements elements, JsonGenerator jg, SerializerProvider serializerProvider) throws IOException { + jg.writeStartObject(); + serializerProvider.defaultSerializeField(name, elements.getElements(), jg); + jg.writeEndObject(); + } + + @Override + public void serialize(AttributeFilterElements elements, JsonGenerator jg, SerializerProvider serializerProvider) throws IOException { + if (elements instanceof UriAttributeFilterElements) { + serializeWrapped(UriAttributeFilterElements.NAME, elements, jg, serializerProvider); + } else if (elements instanceof ValueAttributeFilterElements) { + serializeWrapped(ValueAttributeFilterElements.NAME, elements, jg, serializerProvider); + } else { + serializerProvider.defaultSerializeValue(elements.getElements(), jg); + } + } + } + + class Deserializer extends JsonDeserializer { + + private static List nodeToElements(JsonNode node) { + return StreamSupport.stream(node.spliterator(), false).map(JsonNode::textValue).collect(Collectors.toList()); + } + + @Override + public AttributeFilterElements deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + final JsonNode node = jp.readValueAsTree(); + switch (node.getNodeType()) { + case ARRAY: + return new UriAttributeFilterElements(nodeToElements(node)); + case OBJECT: + final JsonNode uris = node.findValue(UriAttributeFilterElements.NAME); + if (uris != null) { + return new UriAttributeFilterElements(nodeToElements(uris)); + } + final JsonNode values = node.findValue(ValueAttributeFilterElements.NAME); + if (values != null) { + return new ValueAttributeFilterElements(nodeToElements(values)); + } + throw from(jp, "Unknown type of AttributeFilterElements"); + default: + throw from(jp, "Unknown value of type: " + jp.currentToken()); + } + } + } + + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ComparisonCondition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ComparisonCondition.java new file mode 100644 index 000000000..1de8df6f2 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ComparisonCondition.java @@ -0,0 +1,111 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Objects; + +import static org.apache.commons.lang3.Validate.notNull; + +/** + * Condition of {@link MeasureValueFilter} that compares measure values against a single value. + */ +@JsonRootName(ComparisonCondition.NAME) +public class ComparisonCondition extends MeasureValueFilterCondition implements Serializable { + + static final String NAME = "comparison"; + + private static final long serialVersionUID = 2944349621407799356L; + + private final String operator; + private final BigDecimal value; + + @JsonCreator + public ComparisonCondition( + @JsonProperty("operator") final String operator, + @JsonProperty("value") final BigDecimal value, + @JsonProperty("treatNullValuesAs") final BigDecimal treatNullValuesAs + ) { + super(treatNullValuesAs); + this.operator = notNull(operator, "operator"); + this.value = notNull(value, "value"); + } + + /** + * Creates a new {@link ComparisonCondition} instance. + * + * @param operator The operator of the condition . + * @param value The value of the condition. + */ + public ComparisonCondition( + final ComparisonConditionOperator operator, + final BigDecimal value + ) { + this(notNull(operator, "operator").toString(), value, null); + } + + /** + * Creates a new {@link ComparisonCondition} instance. + * + * @param operator The operator of the condition . + * @param value The value of the condition. + * @param treatNullValuesAs The number that will be used instead of compared values that are null. + */ + public ComparisonCondition( + final ComparisonConditionOperator operator, + final BigDecimal value, + final BigDecimal treatNullValuesAs + ) { + this(notNull(operator, "operator").toString(), value, treatNullValuesAs); + } + + /** + * @return comparison condition operator + */ + @JsonIgnore + public ComparisonConditionOperator getOperator() { + return ComparisonConditionOperator.of(operator); + } + + @JsonProperty("operator") + public String getStringOperator() { + return this.operator; + } + + /** + * @return comparison condition value + */ + public BigDecimal getValue() { + return value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + final ComparisonCondition that = (ComparisonCondition) o; + return Objects.equals(operator, that.operator) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), operator, value); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ComparisonConditionOperator.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ComparisonConditionOperator.java new file mode 100644 index 000000000..76a223612 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ComparisonConditionOperator.java @@ -0,0 +1,45 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.joining; + +/** + * Represents all the possible operators of {@link ComparisonCondition} + */ +public enum ComparisonConditionOperator { + GREATER_THAN, + GREATER_THAN_OR_EQUAL_TO, + LESS_THAN, + LESS_THAN_OR_EQUAL_TO, + EQUAL_TO, + NOT_EQUAL_TO; + + @JsonCreator + public static ComparisonConditionOperator of(String operator) { + notNull(operator, "operator"); + try { + return ComparisonConditionOperator.valueOf(operator); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException( + format("Unknown value for comparison condition operator: \"%s\", supported values are: [%s]", + operator, stream(ComparisonConditionOperator.values()).map(Enum::name).collect(joining(","))), + e); + } + } + + @JsonValue + @Override + public String toString() { + return name(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/CompatibilityFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/CompatibilityFilter.java new file mode 100644 index 000000000..fc1596f8a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/CompatibilityFilter.java @@ -0,0 +1,20 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Covers all the AFM execution filters including the "deprecated" ones. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ExpressionFilter.class, name = ExpressionFilter.NAME), + @JsonSubTypes.Type(value = ExtendedFilter.class), +}) +public interface CompatibilityFilter { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/DateFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/DateFilter.java new file mode 100644 index 000000000..84b9e5e52 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/DateFilter.java @@ -0,0 +1,65 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.executeafm.ObjQualifier; + +import java.io.Serializable; +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Represents filter by date. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class DateFilter implements FilterItem, Serializable { + + private static final long serialVersionUID = -804172518160419510L; + private final ObjQualifier dataSet; + + /** + * Creates new filter + * + * @param dataSet qualifier of date dimension dataSet + */ + DateFilter(final ObjQualifier dataSet) { + this.dataSet = notNull(dataSet, "dataSet"); + } + + /** + * @return filtered dataSet qualifier + */ + @JsonProperty + public ObjQualifier getDataSet() { + return dataSet; + } + + @Override + public ObjQualifier getObjQualifier() { + return getDataSet(); + } + + /** + * @return true if no time period is specified, false otherwise + */ + public abstract boolean isAllTimeSelected(); + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DateFilter that = (DateFilter) o; + return Objects.equals(dataSet, that.dataSet); + } + + @Override + public int hashCode() { + return Objects.hash(dataSet); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ExpressionFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ExpressionFilter.java new file mode 100644 index 000000000..df8b94969 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ExpressionFilter.java @@ -0,0 +1,56 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Objects; + +/** + * To be deprecated filter using plain expression + */ +@JsonRootName("expression") +public final class ExpressionFilter implements CompatibilityFilter { + static final String NAME = "expression"; + private final String value; + + /** + * Creates new instance + * + * @param value expression value + */ + @JsonCreator + public ExpressionFilter(@JsonProperty("value") final String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof ExpressionFilter)) + return false; + ExpressionFilter that = (ExpressionFilter) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ExtendedFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ExtendedFilter.java new file mode 100644 index 000000000..ea0726cc4 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ExtendedFilter.java @@ -0,0 +1,18 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = FilterItem.class), + @JsonSubTypes.Type(value = MeasureValueFilter.class, name = MeasureValueFilter.NAME), + @JsonSubTypes.Type(value = RankingFilter.class, name = RankingFilter.NAME), +}) +public interface ExtendedFilter { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/FilterItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/FilterItem.java new file mode 100644 index 000000000..be2525a0b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/FilterItem.java @@ -0,0 +1,41 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; +import com.gooddata.sdk.model.md.Obj; + +/** + * Covers all the filters which can be used within AFM + */ +@JsonSubTypes({ + @JsonSubTypes.Type(value = PositiveAttributeFilter.class, name = PositiveAttributeFilter.NAME), + @JsonSubTypes.Type(value = NegativeAttributeFilter.class, name = NegativeAttributeFilter.NAME), + @JsonSubTypes.Type(value = AbsoluteDateFilter.class, name = AbsoluteDateFilter.NAME), + @JsonSubTypes.Type(value = RelativeDateFilter.class, name = RelativeDateFilter.NAME) +}) +public interface FilterItem extends CompatibilityFilter, ExtendedFilter { + + + /** + * Get qualifier of {@link Obj} to which the filter relates. + * + * @return filtered object qualifier + */ + @JsonIgnore + ObjQualifier getObjQualifier(); + + /** + * Copy itself using given uri qualifier + * + * @param qualifier qualifier to use for the new filter + * @return self copy with given qualifier + */ + FilterItem withObjUriQualifier(UriObjQualifier qualifier); +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/MeasureValueFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/MeasureValueFilter.java new file mode 100644 index 000000000..e42ef87aa --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/MeasureValueFilter.java @@ -0,0 +1,101 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.Qualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; + +import java.io.Serializable; +import java.util.Objects; + +import static org.apache.commons.lang3.Validate.notNull; + +/** + * Represents measure value filter applied on an insight. + */ +@JsonRootName(MeasureValueFilter.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MeasureValueFilter implements ExtendedFilter, CompatibilityFilter, Serializable { + + public static final String NAME = "measureValueFilter"; + + private static final long serialVersionUID = -3038654904981929337L; + + private final Qualifier measure; + private final MeasureValueFilterCondition condition; + + /** + * Creates a new {@link MeasureValueFilter} instance without specified condition {@link MeasureValueFilterCondition} + * It represents use-case with not active filter in insight which could be modified later. + * + * @param measure The qualifier of referenced measure. + */ + public MeasureValueFilter(final Qualifier measure) { + this(measure, null); + } + + /** + * Creates a new {@link MeasureValueFilter} instance. + * + * @param measure The qualifier of referenced measure. + * @param condition The condition applied to a sliced measure value. (Optional) + */ + @JsonCreator + public MeasureValueFilter( + @JsonProperty("measure") final Qualifier measure, + @JsonProperty("condition") final MeasureValueFilterCondition condition) { + this.measure = notNull(measure, "measure"); + this.condition = condition; + } + + /** + * Copy itself using given uri qualifier + * + * @param qualifier qualifier to use for the new filter + * @return self copy with given qualifier + */ + public MeasureValueFilter withUriObjQualifier(final UriObjQualifier qualifier) { + return new MeasureValueFilter(qualifier, this.condition); + } + + /** + * @return qualifier of referenced measure + */ + public Qualifier getMeasure() { + return measure; + } + + /** + * @return condition applied to a sliced measure value + */ + public MeasureValueFilterCondition getCondition() { + return condition; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final MeasureValueFilter that = (MeasureValueFilter) o; + return Objects.equals(measure, that.measure) && + Objects.equals(condition, that.condition); + } + + @Override + public int hashCode() { + return Objects.hash(measure, condition); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/MeasureValueFilterCondition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/MeasureValueFilterCondition.java new file mode 100644 index 000000000..43aa9d4f8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/MeasureValueFilterCondition.java @@ -0,0 +1,55 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Objects; + +/** + * Covers all the conditions that can be used within {@link MeasureValueFilter}. + *

+ * Contains shared functionality to set a custom value instead of {@code null} measure + * values against the condition's value will be compared to. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ComparisonCondition.class, name = ComparisonCondition.NAME), + @JsonSubTypes.Type(value = RangeCondition.class, name = RangeCondition.NAME) +}) +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class MeasureValueFilterCondition implements Serializable { + private static final long serialVersionUID = 7845069496955848265L; + + private final BigDecimal treatNullValuesAs; + + public MeasureValueFilterCondition(final BigDecimal treatNullValuesAs) { + this.treatNullValuesAs = treatNullValuesAs; + } + + @JsonProperty + public BigDecimal getTreatNullValuesAs() { + return treatNullValuesAs; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final MeasureValueFilterCondition that = (MeasureValueFilterCondition) o; + return Objects.equals(treatNullValuesAs, that.treatNullValuesAs); + } + + @Override + public int hashCode() { + return Objects.hash(treatNullValuesAs); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/NegativeAttributeFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/NegativeAttributeFilter.java new file mode 100644 index 000000000..909c66d4f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/NegativeAttributeFilter.java @@ -0,0 +1,103 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; + +import java.util.List; +import java.util.Objects; + +import static java.util.Arrays.asList; + +/** + * Filter of attribute filtering by NOT IN expression + */ +@JsonRootName(NegativeAttributeFilter.NAME) +public class NegativeAttributeFilter extends AttributeFilter { + + static final String NAME = "negativeAttributeFilter"; + private static final long serialVersionUID = -6202625318104289333L; + private final AttributeFilterElements notIn; + + /** + * Creates new instance of given display form and not in list + * + * @param displayForm display form + * @param notIn list of not in elements + * @deprecated for compatibility with version 2.x only, use {@link #NegativeAttributeFilter(ObjQualifier, AttributeFilterElements)} instead + */ + @Deprecated + public NegativeAttributeFilter(@JsonProperty("displayForm") final ObjQualifier displayForm, + @JsonProperty("notIn") final List notIn) { + this(displayForm, new SimpleAttributeFilterElements(notIn)); + } + + /** + * Creates new instance of given display form and not in list + * + * @param displayForm display form + * @param notIn not in elements (uris or values) + */ + @JsonCreator + public NegativeAttributeFilter(@JsonProperty("displayForm") final ObjQualifier displayForm, + @JsonProperty("notIn") final AttributeFilterElements notIn) { + super(displayForm); + this.notIn = notIn; + } + + /** + * @deprecated for compatibility with version 2.x only, use {@link #NegativeAttributeFilter(ObjQualifier, AttributeFilterElements)} instead + */ + @Deprecated + public NegativeAttributeFilter(final ObjQualifier displayForm, final String... notIn) { + this(displayForm, asList(notIn)); + } + + /** + * @return not in elements + */ + public AttributeFilterElements getNotIn() { + return notIn; + } + + /** + * @return true if filter contains no elements, false otherwise + */ + @JsonIgnore + public boolean isAllSelected() { + return notIn.getElements().isEmpty(); + } + + @Override + public FilterItem withObjUriQualifier(final UriObjQualifier qualifier) { + return new NegativeAttributeFilter(qualifier, notIn); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NegativeAttributeFilter that = (NegativeAttributeFilter) o; + return super.equals(that) && Objects.equals(notIn, that.notIn); + } + + @Override + public int hashCode() { + return Objects.hash(notIn, super.hashCode()); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/PositiveAttributeFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/PositiveAttributeFilter.java new file mode 100644 index 000000000..432870895 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/PositiveAttributeFilter.java @@ -0,0 +1,92 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; + +import java.util.List; +import java.util.Objects; + +import static java.util.Arrays.asList; + +/** + * Filter of attribute filtering by IN expression + */ +@JsonRootName(PositiveAttributeFilter.NAME) +public class PositiveAttributeFilter extends AttributeFilter { + + static final String NAME = "positiveAttributeFilter"; + private static final long serialVersionUID = 1934771670274345290L; + private final AttributeFilterElements in; + + /** + * Creates new instance of given display form and in list + * + * @param displayForm display form + * @param in list of in elements + * @deprecated for compatibility with version 2.x only, use {@link #PositiveAttributeFilter(ObjQualifier, AttributeFilterElements)} instead + */ + @Deprecated + public PositiveAttributeFilter(final ObjQualifier displayForm, final List in) { + this(displayForm, new SimpleAttributeFilterElements(in)); + } + + /** + * Creates new instance of given display form and in list + * + * @param displayForm display form + * @param in in elements (uris or values) + */ + @JsonCreator + public PositiveAttributeFilter(@JsonProperty("displayForm") final ObjQualifier displayForm, + @JsonProperty("in") final AttributeFilterElements in) { + super(displayForm); + this.in = in; + } + + /** + * @deprecated for compatibility with version 2.x only, use {@link #PositiveAttributeFilter(ObjQualifier, AttributeFilterElements)} instead + */ + @Deprecated + public PositiveAttributeFilter(final ObjQualifier displayForm, final String... in) { + this(displayForm, asList(in)); + } + + /** + * @return in elements + */ + public AttributeFilterElements getIn() { + return in; + } + + @Override + public FilterItem withObjUriQualifier(final UriObjQualifier qualifier) { + return new PositiveAttributeFilter(qualifier, in); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PositiveAttributeFilter that = (PositiveAttributeFilter) o; + return super.equals(that) && Objects.equals(in, that.in); + } + + @Override + public int hashCode() { + return Objects.hash(in, super.hashCode()); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RangeCondition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RangeCondition.java new file mode 100644 index 000000000..016164c58 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RangeCondition.java @@ -0,0 +1,123 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Objects; + +import static org.apache.commons.lang3.Validate.notNull; + +/** + * Condition of {@link MeasureValueFilter} that compares measure values against two values. + */ +@JsonRootName(RangeCondition.NAME) +public class RangeCondition extends MeasureValueFilterCondition implements Serializable { + + static final String NAME = "range"; + + private static final long serialVersionUID = -1219122779136638864L; + + private final String operator; + private final BigDecimal from; + private final BigDecimal to; + + @JsonCreator + public RangeCondition( + @JsonProperty("operator") final String operator, + @JsonProperty("from") final BigDecimal from, + @JsonProperty("to") final BigDecimal to, + @JsonProperty("treatNullValuesAs") final BigDecimal treatNullValuesAs) { + super(treatNullValuesAs); + this.operator = notNull(operator, "operator"); + this.from = notNull(from, "from"); + this.to = notNull(to, "to"); + } + + /** + * Creates a new {@link RangeCondition} instance. + * + * @param operator The operator of the range condition. + * @param from The left boundary value. + * @param to The right boundary value. + */ + public RangeCondition( + final RangeConditionOperator operator, + final BigDecimal from, + final BigDecimal to) { + this(notNull(operator, "operator").toString(), from, to, null); + } + + /** + * Creates a new {@link RangeCondition} instance. + * + * @param operator The operator of the range condition. + * @param from The left boundary value. + * @param to The right boundary value. + * @param treatNullValuesAs The number that will be used instead of compared values that are null. + */ + public RangeCondition( + final RangeConditionOperator operator, + final BigDecimal from, + final BigDecimal to, + final BigDecimal treatNullValuesAs) { + this(notNull(operator, "operator").toString(), from, to, treatNullValuesAs); + } + + /** + * @return range condition operator + */ + @JsonIgnore + public RangeConditionOperator getOperator() { + return RangeConditionOperator.of(operator); + } + + @JsonProperty("operator") + public String getStringOperator() { + return this.operator; + } + + /** + * @return left boundary of the range + */ + public BigDecimal getFrom() { + return from; + } + + /** + * @return right boundary of the range + */ + public BigDecimal getTo() { + return to; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + final RangeCondition that = (RangeCondition) o; + return Objects.equals(operator, that.operator) && + Objects.equals(from, that.from) && + Objects.equals(to, that.to); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), operator, from, to); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RangeConditionOperator.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RangeConditionOperator.java new file mode 100644 index 000000000..2c895d034 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RangeConditionOperator.java @@ -0,0 +1,41 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.joining; + +/** + * Represents all the possible operators of {@link RangeCondition}. + */ +public enum RangeConditionOperator { + BETWEEN, + NOT_BETWEEN; + + @JsonCreator + public static RangeConditionOperator of(String operator) { + notNull(operator, "operator"); + try { + return RangeConditionOperator.valueOf(operator); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException( + format("Unknown value for range condition operator: \"%s\", supported values are: [%s]", + operator, stream(RangeConditionOperator.values()).map(Enum::name).collect(joining(","))), + e); + } + } + + @JsonValue + @Override + public String toString() { + return name(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RankingFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RankingFilter.java new file mode 100644 index 000000000..709a45f2e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RankingFilter.java @@ -0,0 +1,202 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.IdentifierObjQualifier; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.Qualifier; +import com.gooddata.sdk.model.executeafm.afm.ObjQualifierConverter; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; + +/** + * Represents a ranking filter applied on an insight. + */ +@JsonRootName(RankingFilter.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RankingFilter implements ExtendedFilter, CompatibilityFilter, Serializable { + + public static final String NAME = "rankingFilter"; + + private static final long serialVersionUID = 2642298346540031612L; + + private final List measures; + private final List attributes; + private final String operator; + private final Integer value; + + /** + * Creates a new {@link RankingFilter} instance. + * + * @param measures measures on which is the ranking applied. Must not be null. + * @param attributes attributes that define ranking granularity. Optional, can be null. + * @param operator operator that defines the type of ranking. + * @param value number of requested ranked records. + * @throws NullPointerException thrown when required parameter is not provided. + */ + @JsonCreator + public RankingFilter( + @JsonProperty("measures") final List measures, + @JsonProperty("attributes") final List attributes, + @JsonProperty("operator") final String operator, + @JsonProperty("value") final Integer value) { + this.measures = notNull(measures, "measures must not be null!"); + this.attributes = attributes; + this.operator = notNull(operator, "operator must not be null!"); + this.value = notNull(value, "value must not be null!"); + } + + public RankingFilter( + final List measures, + final List attributes, + final RankingFilterOperator operator, + final Integer value) { + this(measures, attributes, notNull(operator, "operator must not be null!").name(), value); + } + + private static IllegalArgumentException buildExceptionForFailedConversion(final IdentifierObjQualifier qualifierFailedToConvert) { + return new IllegalArgumentException(format("Supplied converter does not provide conversion for '%s'!", qualifierFailedToConvert)); + } + + /** + * Returns all the qualifiers used by the ranking filter. + *

+ * This information comes handy if it is necessary, for example, to convert the ranking filter to use just + * the URI object qualifiers instead of the identifier object qualifiers. It can be used to gather these + * for a conversion service. + * + * @return all the qualifiers the ranking filter uses + */ + @JsonIgnore + public Collection getObjQualifiers() { + return Stream.concat( + this.measures.stream(), + this.attributes == null ? Stream.empty() : this.attributes.stream() + ) + .filter(ObjQualifier.class::isInstance) + .map(ObjQualifier.class::cast) + .collect(Collectors.toSet()); + } + + /** + * Copy itself using the given object qualifier converter in case when {@link IdentifierObjQualifier} instances are used in the object otherwise the + * original object is returned. + *

+ * The provided converter must be able to handle the conversion for the qualifiers that are of the {@link IdentifierObjQualifier} type that are used by + * this object or its encapsulated child objects. + * + * @param objQualifierConverter The function that converts identifier qualifiers to the matching URI qualifiers. In case when the object uses the + * identifier qualifiers, it + * will return a new copy of itself or its encapsulated objects that used URI qualifiers, otherwise the original object is returned. + * The parameter must not be null. + * @return copy of itself with replaced qualifiers in case when some {@link IdentifierObjQualifier} were used, otherwise original object is returned. + * @throws IllegalArgumentException The exception is thrown when conversion for the identifier qualifier used by this ranking filter could not be + * made by the provided + * converter or when provided converter is null. + */ + public RankingFilter withObjUriQualifiers(final ObjQualifierConverter objQualifierConverter) { + notNull(objQualifierConverter, "objQualifierConverter"); + + return new RankingFilter( + translateIdentifierQualifiers(this.measures, objQualifierConverter), + attributes == null ? null : translateIdentifierQualifiers(this.attributes, objQualifierConverter), + operator, + value + ); + } + + private List translateIdentifierQualifiers(final List qualifiers, final ObjQualifierConverter objQualifierConverter) { + return qualifiers.stream() + .map(qualifier -> translateIdentifierQualifier(qualifier, objQualifierConverter)) + .collect(Collectors.toList()); + } + + private Qualifier translateIdentifierQualifier(final Qualifier qualifier, final ObjQualifierConverter objQualifierConverter) { + if (qualifier instanceof IdentifierObjQualifier) { + final IdentifierObjQualifier identifierQualifierToConvert = (IdentifierObjQualifier) qualifier; + return objQualifierConverter.convertToUriQualifier(identifierQualifierToConvert) + .orElseThrow(() -> buildExceptionForFailedConversion(identifierQualifierToConvert)); + } + return qualifier; + } + + /** + * @return measures on which is the ranking applied + */ + public List getMeasures() { + return measures; + } + + /** + * @return granularity of the ranking + */ + public List getAttributes() { + return attributes; + } + + /** + * Get operator as an enum constant for easier programmatic access. + * + * @return ranking operator constant + */ + @JsonIgnore + public RankingFilterOperator getOperator() { + return RankingFilterOperator.of(this.operator); + } + + /** + * Get operator as a string representation of the {@link RankingFilterOperator} enum constant as it was parsed from the JSON. + * + * @return string operator provided at the time of the filter instance creation + */ + @JsonProperty("operator") + public String getOperatorAsString() { + return operator; + } + + /** + * @return number of ranked records + */ + public Integer getValue() { + return value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final RankingFilter that = (RankingFilter) o; + return Objects.equals(measures, that.measures) && + Objects.equals(attributes, that.attributes) && + Objects.equals(operator, that.operator) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(measures, attributes, operator, value); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RankingFilterOperator.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RankingFilterOperator.java new file mode 100644 index 000000000..4a532fc70 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RankingFilterOperator.java @@ -0,0 +1,42 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.joining; + +/** + * Represents all the possible operators of {@link RankingFilter}. + */ +public enum RankingFilterOperator { + TOP, + BOTTOM; + + @JsonCreator + public static RankingFilterOperator of(final String operator) { + notNull(operator, "operator"); + try { + return RankingFilterOperator.valueOf(operator); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException( + format("Unknown value for ranking filter operator: \"%s\", supported values are: [%s]", + operator, stream(RankingFilterOperator.values()).map(Enum::name).collect(joining(","))), + e); + } + } + + @JsonValue + @Override + public String toString() { + return name(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RelativeDateFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RelativeDateFilter.java new file mode 100644 index 000000000..0cef6d2a7 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/RelativeDateFilter.java @@ -0,0 +1,93 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; + +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + + +/** + * Represents {@link DateFilter} specifying relative range of given granularity. + */ +@JsonRootName(RelativeDateFilter.NAME) +public class RelativeDateFilter extends DateFilter { + + static final String NAME = "relativeDateFilter"; + private static final long serialVersionUID = 7257627800833737063L; + private final String granularity; + private final Integer from; + private final Integer to; + + /** + * Creates new instance + * + * @param dataSet qualifier of date dimension dataSet + * @param granularity granularity specified as type GDC date attribute type + * @param from from + * @param to to + */ + @JsonCreator + public RelativeDateFilter(@JsonProperty("dataSet") final ObjQualifier dataSet, + @JsonProperty("granularity") final String granularity, + @JsonProperty("from") final Integer from, @JsonProperty("to") final Integer to) { + super(dataSet); + this.granularity = notEmpty(granularity, "granularity"); + this.from = from; + this.to = to; + } + + public String getGranularity() { + return granularity; + } + + public Integer getFrom() { + return from; + } + + public Integer getTo() { + return to; + } + + @Override + public FilterItem withObjUriQualifier(final UriObjQualifier qualifier) { + return new RelativeDateFilter(qualifier, granularity, from, to); + } + + @Override + @JsonIgnore + public boolean isAllTimeSelected() { + return getFrom() == null || getTo() == null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RelativeDateFilter that = (RelativeDateFilter) o; + return super.equals(that) && Objects.equals(granularity, that.granularity) && + Objects.equals(from, that.from) && + Objects.equals(to, that.to); + } + + @Override + public int hashCode() { + return Objects.hash(granularity, from, to, super.hashCode()); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/SimpleAttributeFilterElements.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/SimpleAttributeFilterElements.java new file mode 100644 index 000000000..dce54c676 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/SimpleAttributeFilterElements.java @@ -0,0 +1,36 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import java.io.Serializable; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * {@link AttributeFilterElements} represented by simple array. + * + * @deprecated It has the same semantic as {@link UriAttributeFilterElements}. + * Preserved because of compatibility and will be removed in future API version. + */ +@Deprecated +public class SimpleAttributeFilterElements implements AttributeFilterElements, Serializable { + + private static final long serialVersionUID = -2935674265292888490L; + + private final List elements; + + public SimpleAttributeFilterElements(final List elements) { + this.elements = notNull(elements, "elements"); + } + + @Override + public List getElements() { + return elements; + } + + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/UriAttributeFilterElements.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/UriAttributeFilterElements.java new file mode 100644 index 000000000..c7d5aa4dd --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/UriAttributeFilterElements.java @@ -0,0 +1,71 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +import static java.util.Arrays.asList; + +/** + * {@link AttributeFilterElements} represented by uris of elements. + */ +public final class UriAttributeFilterElements implements AttributeFilterElements, Serializable { + + static final String NAME = "uris"; + private static final long serialVersionUID = -588170788038973574L; + private final List uris; + + /** + * Creates new instance of given attribute elements' uris. + * + * @param uris elements' uris. + */ + @JsonCreator + public UriAttributeFilterElements(final List uris) { + this.uris = uris; + } + + /** + * Creates new instance of given attribute elements' uris. + * + * @param uris elements' uris. + */ + public UriAttributeFilterElements(String... uris) { + this(asList(uris)); + } + + public List getUris() { + return uris; + } + + @Override + public List getElements() { + return getUris(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UriAttributeFilterElements that = (UriAttributeFilterElements) o; + return Objects.equals(uris, that.uris); + } + + @Override + public int hashCode() { + return Objects.hash(uris); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ValueAttributeFilterElements.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ValueAttributeFilterElements.java new file mode 100644 index 000000000..d46faa45d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/afm/filter/ValueAttributeFilterElements.java @@ -0,0 +1,71 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.afm.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +import static java.util.Arrays.asList; + +/** + * {@link AttributeFilterElements} represented by values of elements. + */ +public final class ValueAttributeFilterElements implements AttributeFilterElements, Serializable { + + static final String NAME = "values"; + private static final long serialVersionUID = 8162844914489089022L; + private final List values; + + /** + * Creates new instance of given attribute elements' values. + * + * @param values elements' values. + */ + @JsonCreator + public ValueAttributeFilterElements(final List values) { + this.values = values; + } + + /** + * Creates new instance of given attribute elements' values. + * + * @param values elements' values. + */ + public ValueAttributeFilterElements(String... values) { + this(asList(values)); + } + + public List getValues() { + return values; + } + + @Override + public List getElements() { + return getValues(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ValueAttributeFilterElements that = (ValueAttributeFilterElements) o; + return Objects.equals(values, that.values); + } + + @Override + public int hashCode() { + return Objects.hash(values); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/AttributeHeader.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/AttributeHeader.java new file mode 100644 index 000000000..606ea32c8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/AttributeHeader.java @@ -0,0 +1,163 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.afm.Afm; +import com.gooddata.sdk.model.executeafm.afm.AttributeItem; +import com.gooddata.sdk.model.executeafm.afm.LocallyIdentifiable; + +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Header of an attribute. + */ +@JsonRootName(AttributeHeader.NAME) +public class AttributeHeader implements Header, LocallyIdentifiable { + + static final String NAME = "attributeHeader"; + + private final String name; + private final String localIdentifier; + private final String uri; + private final String identifier; + private final String type; + private final AttributeInHeader formOf; + + private List totalItems; + + /** + * Creates new header + * + * @param name name + * @param localIdentifier local identifier + * @param uri uri + * @param identifier identifier + * @param formOf info about attribute which this header's display form is form of + */ + public AttributeHeader(final String name, final String localIdentifier, final String uri, final String identifier, final AttributeInHeader formOf) { + this(name, localIdentifier, uri, identifier, formOf, null); + } + + /** + * Creates new header + * + * @param name name + * @param localIdentifier local identifier + * @param uri uri + * @param identifier identifier + * @param formOf info about attribute which this header's display form is form of + * @param totalHeaderItems total header items + */ + public AttributeHeader(@JsonProperty("name") final String name, + @JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("uri") final String uri, + @JsonProperty("identifier") final String identifier, + @JsonProperty("formOf") final AttributeInHeader formOf, + @JsonProperty("totalItems") final List totalHeaderItems) { + this(name, localIdentifier, uri, identifier, null, formOf, totalHeaderItems); + } + + /** + * Creates new header + * + * @param name name + * @param localIdentifier local identifier + * @param uri uri + * @param identifier identifier + * @param type type + * @param formOf info about attribute which this header's display form is form of + * @param totalHeaderItems total header items + */ + @JsonCreator + public AttributeHeader(@JsonProperty("name") final String name, + @JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("uri") final String uri, + @JsonProperty("identifier") final String identifier, + @JsonProperty("type") final String type, + @JsonProperty("formOf") final AttributeInHeader formOf, + @JsonProperty("totalItems") final List totalHeaderItems) { + this.name = notEmpty(name, "name"); + this.localIdentifier = notEmpty(localIdentifier, "localIdentifier"); + this.uri = notEmpty(uri, "uri"); + this.identifier = notEmpty(identifier, "identifier"); + this.type = type; + this.formOf = notNull(formOf, "formOf"); + this.totalItems = totalHeaderItems; + } + + /** + * Header name, an attribute's display form title, or specified alias. + * + * @return header name + */ + public String getName() { + return name; + } + + /** + * Local identifier referencing header's {@link AttributeItem} + * within {@link Afm} + * + * @return attribute's local identifier + */ + @Override + public String getLocalIdentifier() { + return localIdentifier; + } + + /** + * Uri of attribute's display form + * + * @return uri + */ + public String getUri() { + return uri; + } + + /** + * Metadata identifier of attribute's display form + * + * @return identifier + */ + public String getIdentifier() { + return identifier; + } + + /** + * Metadata type of attribute's display form + * + * @return type + */ + public String getType() { + return type; + } + + public AttributeInHeader getFormOf() { + return formOf; + } + + /** + * Totals' headers belonging to the same level as this header. + * + * @return lists of totals' header + */ + public List getTotalItems() { + return totalItems; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/AttributeInHeader.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/AttributeInHeader.java new file mode 100644 index 000000000..0b5628c61 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/AttributeInHeader.java @@ -0,0 +1,85 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Represents attribute in {@link AttributeHeader} + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AttributeInHeader { + private final String name; + private final String uri; + private final String identifier; + + /** + * Creates new instance + * + * @param name attribute's title + * @param uri attribute's uri + * @param identifier attribute's identifier + */ + @JsonCreator + public AttributeInHeader(@JsonProperty("name") final String name, + @JsonProperty("uri") final String uri, + @JsonProperty("identifier") final String identifier) { + this.name = notEmpty(name, "name"); + this.uri = notEmpty(uri, "uri"); + this.identifier = notEmpty(identifier, "identifier"); + } + + /** + * @return attribute's title + */ + public String getName() { + return name; + } + + /** + * @return attribute's uri + */ + public String getUri() { + return uri; + } + + /** + * @return attribute's identifier + */ + public String getIdentifier() { + return identifier; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AttributeInHeader attributeInHeader = (AttributeInHeader) o; + + if (name != null ? !name.equals(attributeInHeader.name) : attributeInHeader.name != null) return false; + if (uri != null ? !uri.equals(attributeInHeader.uri) : attributeInHeader.uri != null) return false; + return identifier != null ? identifier.equals(attributeInHeader.identifier) : attributeInHeader.identifier == null; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (uri != null ? uri.hashCode() : 0); + result = 31 * result + (identifier != null ? identifier.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/ExecutionResponse.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/ExecutionResponse.java new file mode 100644 index 000000000..daf20ac38 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/ExecutionResponse.java @@ -0,0 +1,92 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.Execution; +import com.gooddata.sdk.model.executeafm.result.ExecutionResult; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Represents response on {@link Execution} request. + * Provides the dimensions with headers and the (polling) uri to {@link ExecutionResult} + * (so called dataResult). + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("executionResponse") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ExecutionResponse { + + private static final String EXECUTION_RESULT_LINK = "executionResult"; + + private final List dimensions; + private final Map links; + + /** + * Creates new instance of given dimensions and execution result uri. + * + * @param dimensions dimensions + * @param executionResultUri execution result uri + */ + public ExecutionResponse(final List dimensions, final String executionResultUri) { + this(dimensions, new LinkedHashMap<>()); + links.put(EXECUTION_RESULT_LINK, notEmpty(executionResultUri, "executionResultUri")); + } + + @JsonCreator + private ExecutionResponse(@JsonProperty("dimensions") final List dimensions, + @JsonProperty("links") final Map links) { + this.dimensions = notNull(dimensions, "dimensions"); + this.links = notNull(links, "links"); + } + + /** + * List of dimensions describing the result. + * + * @return dimensions + */ + public List getDimensions() { + return dimensions; + } + + /** + * Map of response's links. + * + * @return links + */ + public Map getLinks() { + return links; + } + + /** + * Uri referencing the data result location. + * + * @return execution result uri or throws exception in case the link doesn't exist + */ + @JsonIgnore + public String getExecutionResultUri() { + return notNullState(notNullState(links, "links").get(EXECUTION_RESULT_LINK), EXECUTION_RESULT_LINK); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/Header.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/Header.java new file mode 100644 index 000000000..6830f752c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/Header.java @@ -0,0 +1,22 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * {@link ResultDimension}'s header. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = MeasureGroupHeader.class, name = MeasureGroupHeader.NAME), + @JsonSubTypes.Type(value = AttributeHeader.class, name = AttributeHeader.NAME) +}) +public interface Header { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/MeasureGroupHeader.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/MeasureGroupHeader.java new file mode 100644 index 000000000..cd5ad03d6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/MeasureGroupHeader.java @@ -0,0 +1,48 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +/** + * Header of a measure group. + */ +@JsonRootName(MeasureGroupHeader.NAME) +public class MeasureGroupHeader implements Header { + + static final String NAME = "measureGroupHeader"; + + private final List items; + + /** + * Creates new header + * + * @param items header items + */ + @JsonCreator + public MeasureGroupHeader(@JsonProperty("items") final List items) { + this.items = items; + } + + /** + * Header items for particular measures + * + * @return header items + */ + public List getItems() { + return items; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/MeasureHeaderItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/MeasureHeaderItem.java new file mode 100644 index 000000000..9e89c228e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/MeasureHeaderItem.java @@ -0,0 +1,116 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.afm.Afm; +import com.gooddata.sdk.model.executeafm.afm.LocallyIdentifiable; +import com.gooddata.sdk.model.executeafm.afm.MeasureItem; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Header of particular measure. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("measureHeaderItem") +public class MeasureHeaderItem implements LocallyIdentifiable { + + private final String name; + private final String format; + private final String localIdentifier; + private String uri; + private String identifier; + + public MeasureHeaderItem(final String name, final String format, final String localIdentifier) { + this.name = name; + this.format = format; + this.localIdentifier = localIdentifier; + } + + @JsonCreator + public MeasureHeaderItem(@JsonProperty("name") final String name, + @JsonProperty("format") final String format, + @JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("uri") final String uri, + @JsonProperty("identifier") final String identifier) { + this.name = notEmpty(name, "name"); + this.format = notEmpty(format, "format"); + this.localIdentifier = notEmpty(localIdentifier, "localIdentifier"); + this.uri = uri; + this.identifier = identifier; + } + + /** + * Header name, can be measure title, or specified alias + * + * @return name + */ + public String getName() { + return name; + } + + /** + * @return Measure format + */ + public String getFormat() { + return format; + } + + /** + * Local identifier, referencing the {@link MeasureItem} + * in {@link Afm} + * + * @return local identifier + */ + @Override + public String getLocalIdentifier() { + return localIdentifier; + } + + /** + * @return Measure uri + */ + public String getUri() { + return uri; + } + + /** + * Set measure uri + * + * @param uri measure uri + */ + public void setUri(final String uri) { + this.uri = uri; + } + + /** + * @return Measure metadata identifier + */ + public String getIdentifier() { + return identifier; + } + + /** + * Set measure metadata identifier + * + * @param identifier + */ + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/ResultDimension.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/ResultDimension.java new file mode 100644 index 000000000..a25bfb91d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/ResultDimension.java @@ -0,0 +1,55 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.result.ExecutionResult; + +import java.util.List; + +import static java.util.Arrays.asList; + +/** + * Describes single dimension of the {@link ExecutionResult}. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ResultDimension { + private final List

headers; + + /** + * Creates the result dimension of given headers + * + * @param headers headers + */ + @JsonCreator + public ResultDimension(@JsonProperty("headers") final List
headers) { + this.headers = headers; + } + + /** + * Creates the result dimension of given headers + * + * @param headers headers + */ + public ResultDimension(final Header... headers) { + this(asList(headers)); + } + + /** + * @return dimension headers + */ + public List
getHeaders() { + return headers; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/TotalHeaderItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/TotalHeaderItem.java new file mode 100644 index 000000000..5eb38cc67 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/response/TotalHeaderItem.java @@ -0,0 +1,56 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.report.Total; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Header of total + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("totalHeaderItem") +public class TotalHeaderItem { + + private final String name; + + /** + * Creates new header + * + * @param name total name + */ + @JsonCreator + public TotalHeaderItem(@JsonProperty("name") final String name) { + this.name = name; + } + + /** + * Creates new header + * + * @param total total value + */ + public TotalHeaderItem(final Total total) { + this(notNull(total, "total").toString()); + } + + /** + * @return name of total + */ + public String getName() { + return name; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/AttributeHeaderItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/AttributeHeaderItem.java new file mode 100644 index 000000000..8971ded4c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/AttributeHeaderItem.java @@ -0,0 +1,48 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.model.executeafm.result.AttributeHeaderItem.NAME; + +/** + * Header item for attribute + */ +@JsonRootName(NAME) +public class AttributeHeaderItem extends ResultHeaderItem { + + static final String NAME = "attributeHeaderItem"; + + private final String uri; + + /** + * Creates new header item + * + * @param name name of item (usually attribute element title) + * @param uri uri of item (usually attribute element uri) + */ + @JsonCreator + public AttributeHeaderItem(@JsonProperty("name") final String name, @JsonProperty("uri") final String uri) { + super(name); + this.uri = uri; + } + + /** + * @return item uri + */ + public String getUri() { + return uri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Data.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Data.java new file mode 100644 index 000000000..e8638cf83 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Data.java @@ -0,0 +1,106 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.Spliterator; +import java.util.stream.Collectors; + +import static java.util.Spliterators.spliteratorUnknownSize; +import static java.util.stream.StreamSupport.stream; + +/** + * Data of {@link ExecutionResult}, can be of three basic kinds - {@link #NULL}, list and simple value. + */ +@JsonDeserialize(using = Data.DataDeserializer.class) +public interface Data { + + Data NULL = new Data() { + @Override + public boolean isList() { + return false; + } + + @Override + public boolean isValue() { + return false; + } + + @Override + @JsonValue + public String textValue() { + return null; + } + }; + + /** + * @return true if this instance is of kind list, false otherwise + */ + boolean isList(); + + /** + * @return true if this instance is of kind value, false otherwise + */ + boolean isValue(); + + /** + * @return true if this instance is the same as {@link #NULL}, false otherwise + */ + default boolean isNull() { + return this == NULL; + } + + /** + * @return text data value, throws exception for data which can't be represented as text + */ + String textValue(); + + /** + * @return this instance cast to List, may throw exception if this instance is not of kind list. + */ + default List asList() { + throw new UnsupportedOperationException("This is not a list"); + } + + class DataDeserializer extends JsonDeserializer { + @Override + public Data deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException { + final JsonNode root = jp.readValueAsTree(); + if (root.isArray()) { + final List list = stream(spliteratorUnknownSize(root.elements(), Spliterator.ORDERED), false) + .map(elem -> { + try { + return ctxt.readValue(elem.traverse(jp.getCodec()), Data.class); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }).collect(Collectors.toList()); + return new DataList(list); + } else if (root.isTextual()) { + return new DataValue(root.textValue()); + } else if (root.isNull()) { + return NULL; + } else { + throw JsonMappingException.from(jp, "Unknown value of type: " + root.getNodeType()); + } + } + + @Override + public Data getNullValue(final DeserializationContext ctxt) throws JsonMappingException { + return NULL; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/DataList.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/DataList.java new file mode 100644 index 000000000..ba2da8143 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/DataList.java @@ -0,0 +1,78 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.util.stream.Collectors.toList; + +/** + * List value type of {@link Data}, basically wrapper for list of nested {@link Data} + */ +public class DataList extends ArrayList implements Data { + + /** + * Creates new instance of given list of data + * + * @param values list to use as values, can't be null + */ + public DataList(final List values) { + super(notNull(values, "values")); + } + + /** + * Creates new instance by transforming the given array to list of simple or null values + * + * @param array array of values + */ + DataList(final String[] array) { + this(Arrays.stream(array).map(DataList::simpleValue).collect(toList())); + } + + /** + * Creates new instance by transforming the given array to list of data lists of simple or null values + * + * @param array array of values + */ + DataList(final String[][] array) { + this(Arrays.stream(array).map(DataList::new).collect(toList())); + } + + private static Data simpleValue(final String value) { + return value == null ? NULL : new DataValue(value); + } + + @Override + public boolean isList() { + return true; + } + + @Override + public boolean isValue() { + return false; + } + + @Override + public String textValue() { + throw new UnsupportedOperationException("DataList doesn't contain text value"); + } + + @Override + public List asList() { + return this; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/DataValue.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/DataValue.java new file mode 100644 index 000000000..2ed9372bb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/DataValue.java @@ -0,0 +1,68 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonValue; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Simple value type of {@link Data}. Wrapper of textual value. + */ +public class DataValue implements Data { + + private final String value; + + /** + * Creates new instance of given value. + * + * @param value textual value, can't be null + */ + public DataValue(final String value) { + this.value = notNull(value, "value"); + } + + @JsonValue + private String getValue() { + return value; + } + + @Override + public boolean isList() { + return false; + } + + @Override + public boolean isValue() { + return true; + } + + @Override + public String textValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DataValue dataValue = (DataValue) o; + + return value != null ? value.equals(dataValue.value) : dataValue.value == null; + } + + @Override + public int hashCode() { + return value != null ? value.hashCode() : 0; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ExecutionResult.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ExecutionResult.java new file mode 100644 index 000000000..82a1f656d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ExecutionResult.java @@ -0,0 +1,183 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.Execution; + +import java.util.ArrayList; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Data result of the {@link Execution}. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("executionResult") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ExecutionResult { + + private final DataList data; + private final Paging paging; + + private List>> headerItems; + private List>> totals; + private List>> totalTotals; + private List warnings; + + /** + * Creates new result + * + * @param data result data + * @param paging result paging + */ + public ExecutionResult(final String[] data, final Paging paging) { + this.data = new DataList(notNull(data, "data")); + this.paging = notNull(paging, "paging"); + } + + /** + * Creates new result + * + * @param data result data + * @param paging result paging + */ + public ExecutionResult(final String[][] data, final Paging paging) { + this.data = new DataList(notNull(data, "data")); + this.paging = notNull(paging, "paging"); + } + + /** + * Creates new result + * + * @param data result data + * @param paging result paging + * @param headerItems items for headers, for each header in each dimension, there is a list of header items + * @param totals data of totals, for each total in each dimension, there is a list of total's values + */ + @JsonCreator + ExecutionResult(@JsonProperty("data") final DataList data, + @JsonProperty("paging") final Paging paging, + @JsonProperty("headerItems") final List>> headerItems, + @JsonProperty("totals") final List>> totals, + @JsonProperty("totalTotals") final List>> totalTotals, + @JsonProperty("warnings") final List warnings) { + this.data = data; + this.paging = paging; + this.headerItems = headerItems; + this.totals = totals; + this.totalTotals = totalTotals; + this.warnings = warnings; + } + + /** + * @return result data + */ + public DataList getData() { + return data; + } + + /** + * @return result paging + */ + public Paging getPaging() { + return paging; + } + + /** + * @return header items, for each header in each dimension, there is a list of header items + */ + public List>> getHeaderItems() { + return headerItems; + } + + /** + * Sets header items, for each header in each dimension, there is a list of header items + * + * @param headerItems header items + */ + public void setHeaderItems(final List>> headerItems) { + this.headerItems = headerItems; + } + + /** + * Add header items for next dimension (this method will add dimension in header items) + * + * @param items header items for one dimension + */ + public void addHeaderItems(final List> items) { + if (headerItems == null) { + setHeaderItems(new ArrayList<>()); + } + headerItems.add(items); + } + + /** + * @return data of totals, for each total in each dimension, there is a list of total's values + */ + public List>> getTotals() { + return totals; + } + + /** + * Sets total data, for each total in each dimension, there is a list of total's values + * + * @param totals totals data + */ + public void setTotals(final List>> totals) { + this.totals = totals; + } + + /** + * Gets totals of totals data. + * The totals of totals represent intersection between totals in multiple dimensions. + * For each dimension and total combination, there is a list of totals of totals values. + * + * @return 3-dimensional matrix of totals of totals data + */ + public List>> getTotalTotals() { + return totalTotals; + } + + /** + * Sets totals of totals data. + * The totals of totals represent intersection between totals in multiple dimensions. + * For each dimension and total combination, there is a list of totals of totals values. + * + * @param totalTotals 3-dimensional matrix of totals of totals data + */ + public ExecutionResult setTotalTotals(List>> totalTotals) { + this.totalTotals = totalTotals; + return this; + } + + /** + * @return result's warnings + */ + public List getWarnings() { + return warnings; + } + + /** + * Sets warnings for this result + * + * @param warnings result's warning + */ + public void setWarnings(final List warnings) { + this.warnings = warnings; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Paging.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Paging.java new file mode 100644 index 000000000..0443db0c9 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Paging.java @@ -0,0 +1,106 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static java.util.Arrays.asList; +import static org.apache.commons.lang3.ArrayUtils.toObject; + +/** + * Paging of {@link ExecutionResult}. Represents paging in multiple dimensions. + */ +public class Paging { + private List count; + private List offset; + private List total; + + /** + * Creates new paging + */ + public Paging() { + } + + /** + * Creates new paging + * + * @param count multiple dimensions count + * @param offset multiple dimensions offset + * @param total multiple dimensions total + */ + @JsonCreator + public Paging(@JsonProperty("count") final List count, + @JsonProperty("offset") final List offset, + @JsonProperty("total") final List total) { + this.count = notEmpty(count, "count"); + this.offset = notEmpty(offset, "offset"); + this.total = notEmpty(total, "total"); + } + + /** + * @return multiple dimensions count + */ + public List getCount() { + return count; + } + + /** + * @return multiple dimensions offset + */ + public List getOffset() { + return offset; + } + + /** + * @return multiple dimensions total + */ + public List getTotal() { + return total; + } + + /** + * Sets count compound of given elements, each element per dimension + * + * @param count count elements + * @return this + */ + public Paging count(final int... count) { + this.count = asList(toObject(count)); + return this; + } + + /** + * Sets size compound of given elements, each element per dimension + * + * @param total size elements + * @return this + */ + public Paging total(final int... total) { + this.total = asList(toObject(total)); + return this; + } + + /** + * Sets size compound of given elements, each element per dimension + * + * @param offset size elements + * @return this + */ + public Paging offset(final int... offset) { + this.offset = asList(toObject(offset)); + return this; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultHeaderItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultHeaderItem.java new file mode 100644 index 000000000..35ad52a8e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultHeaderItem.java @@ -0,0 +1,42 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Represent header items available in {@link ExecutionResult} + */ +@JsonSubTypes({ + @JsonSubTypes.Type(value = AttributeHeaderItem.class, name = AttributeHeaderItem.NAME), + @JsonSubTypes.Type(value = ResultMeasureHeaderItem.class, name = ResultMeasureHeaderItem.NAME), + @JsonSubTypes.Type(value = ResultTotalHeaderItem.class, name = ResultTotalHeaderItem.NAME) +}) +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public abstract class ResultHeaderItem { + + private final String name; + + protected ResultHeaderItem(final String name) { + this.name = notNull(name, "name"); + } + + /** + * @return header item name + */ + public String getName() { + return name; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultMeasureHeaderItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultMeasureHeaderItem.java new file mode 100644 index 000000000..ad33f4482 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultMeasureHeaderItem.java @@ -0,0 +1,42 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import static com.gooddata.sdk.model.executeafm.result.ResultMeasureHeaderItem.NAME; + +/** + * Header item for measure + */ +@JsonRootName(NAME) +public class ResultMeasureHeaderItem extends ResultHeaderItem { + + static final String NAME = "measureHeaderItem"; + + private final int order; + + /** + * Creates new instance of given header name and order + * + * @param name header name + * @param order measure order within measureGroup + */ + @JsonCreator + public ResultMeasureHeaderItem(@JsonProperty("name") final String name, @JsonProperty("order") final int order) { + super(name); + this.order = order; + } + + /** + * @return measure order within measureGroup + */ + public int getOrder() { + return order; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultTotalHeaderItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultTotalHeaderItem.java new file mode 100644 index 000000000..8eaa8e1dc --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/ResultTotalHeaderItem.java @@ -0,0 +1,74 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.model.md.report.Total; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.model.executeafm.result.ResultTotalHeaderItem.NAME; + +/** + * Header item for total. + */ +@JsonRootName(NAME) +public class ResultTotalHeaderItem extends ResultHeaderItem { + + static final String NAME = "totalHeaderItem"; + + private final String type; + + /** + * Creates new instance of given total type, type is used for the name as well + * + * @param type total type + */ + public ResultTotalHeaderItem(final String type) { + this(type, type); + } + + /** + * Creates new instance of given total type, type is used for the name as well + * + * @param type total type + */ + public ResultTotalHeaderItem(final Total type) { + this(notNull(type, "type").toString()); + } + + /** + * Creates new instance of given header name and total type + * + * @param name header name + * @param type total type + */ + @JsonCreator + public ResultTotalHeaderItem(@JsonProperty("name") final String name, @JsonProperty("type") final String type) { + super(name); + this.type = notEmpty(type, "type"); + } + + /** + * Creates new instance of given header name and total type + * + * @param name header name + * @param type total type + */ + public ResultTotalHeaderItem(final String name, final Total type) { + super(name); + this.type = notNull(type, "type").toString(); + } + + /** + * @return type of total + */ + public String getType() { + return type; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Warning.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Warning.java new file mode 100644 index 000000000..33a9b0e47 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/result/Warning.java @@ -0,0 +1,97 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.result; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.util.Collections.emptyList; + +/** + * {@link ExecutionResult}'s warning. + */ +public class Warning { + private final String warningCode; + private final String message; + private final List parameters; + + /** + * Creates new instance + * + * @param warningCode error code + * @param message message + */ + public Warning(final String warningCode, final String message) { + this(warningCode, message, emptyList()); + } + + /** + * Creates new instance + * + * @param warningCode error code + * @param message message + * @param parameters message's parameters + */ + @JsonCreator + public Warning(@JsonProperty("warningCode") final String warningCode, + @JsonProperty("message") final String message, + @JsonProperty("parameters") final List parameters) { + this.warningCode = notEmpty(warningCode, "warningCode"); + this.message = notEmpty(message, "message"); + this.parameters = notNull(parameters, "parameters"); + } + + /** + * @return warning's error code + */ + public String getWarningCode() { + return warningCode; + } + + /** + * @return warning's message + */ + public String getMessage() { + return message; + } + + /** + * @return message's parameters + */ + public List getParameters() { + return parameters; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Warning warning = (Warning) o; + + if (warningCode != null ? !warningCode.equals(warning.warningCode) : warning.warningCode != null) return false; + if (message != null ? !message.equals(warning.message) : warning.message != null) return false; + return parameters != null ? parameters.equals(warning.parameters) : warning.parameters == null; + } + + @Override + public int hashCode() { + int result = warningCode != null ? warningCode.hashCode() : 0; + result = 31 * result + (message != null ? message.hashCode() : 0); + result = 31 * result + (parameters != null ? parameters.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeLocatorItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeLocatorItem.java new file mode 100644 index 000000000..0f185c2e5 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeLocatorItem.java @@ -0,0 +1,48 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +/** + * Holds attribute including it's specific element + */ +@JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.NAME) +@JsonTypeName("attributeLocatorItem") +public class AttributeLocatorItem implements LocatorItem { + private final String attributeIdentifier; + private final String element; + + @JsonCreator + public AttributeLocatorItem( + @JsonProperty("attributeIdentifier") final String attributeIdentifier, + @JsonProperty("element") final String element) { + this.attributeIdentifier = attributeIdentifier; + this.element = element; + } + + public String getAttributeIdentifier() { + return attributeIdentifier; + } + + public String getElement() { + return element; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeSortAggregation.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeSortAggregation.java new file mode 100644 index 000000000..7609010d6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeSortAggregation.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Represents aggregation used when putting together all related + * values belonging to attribute element when we are sorting by this + * attribute. You can find more details in {@link AttributeSortItem}. + */ +public enum AttributeSortAggregation { + SUM; + + @JsonValue + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeSortItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeSortItem.java new file mode 100644 index 000000000..cf23e0605 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/AttributeSortItem.java @@ -0,0 +1,99 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Define sort by specific attribute + * + *

With "aggregation" active you can sort all elements of attribute + * by "aggregation fn" applied to all valid values belonging to each + * element. This is extremely useful when sorting stacked + * visualizations like stack bar/area charts. Currently supported is + * only "sum", see {@link AttributeSortAggregation}

+ * + *

Simple example (dimension = Year, measureGroup; 2 metrics; sort + * on Year with aggregation="sum", descending):

+ *
+ * Year       2006        2007
+ * Names     M1  M2      M1   M2
+ * Values    1    2       3    4
+ * 
+ * + *

We take all values belonging to each attribute element of chosen attribute + * and apply selected function (sum) on them. Notice that we are summarising + * values from different metrics:

+ *
+ * 2006 (1 + 2 = 3)
+ * 2007 (3 + 4 = 7)
+ * 
+ * + *

After that we shuffle year attribute elements related to results from "sum" + * function:

+ *
+ * Year       2007        2006
+ * Names     M1  M2      M1   M2
+ * Values    3    4       1    2
+ * 
+ */ +@JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.NAME) +@JsonTypeName("attributeSortItem") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AttributeSortItem implements SortItem { + + private final String direction; + private final String attributeIdentifier; + private final String aggregation; + + @JsonCreator + public AttributeSortItem(@JsonProperty("direction") final String direction, + @JsonProperty("attributeIdentifier") final String attributeIdentifier, + @JsonProperty("aggregation") final String aggregation) { + this.attributeIdentifier = attributeIdentifier; + this.direction = direction; + this.aggregation = aggregation; + } + + public AttributeSortItem(final String direction, + final String attributeIdentifier) { + this(direction, attributeIdentifier, null); + } + + public AttributeSortItem(final Direction direction, final String attributeIdentifier) { + this(notNull(direction, "direction").toString(), attributeIdentifier, null); + } + + public AttributeSortItem(final Direction direction, final String attributeIdentifier, final AttributeSortAggregation aggregation) { + this(notNull(direction, "direction").toString(), attributeIdentifier, notNull(aggregation, "aggregation").toString()); + } + + public String getDirection() { + return direction; + } + + public String getAttributeIdentifier() { + return attributeIdentifier; + } + + public String getAggregation() { + return aggregation; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/Dimension.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/Dimension.java new file mode 100644 index 000000000..dd3f14a95 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/Dimension.java @@ -0,0 +1,104 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.afm.Afm; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.util.Arrays.asList; + +/** + * Dimension content definition. Dimension contains one or more attributes. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Dimension { + + /** + * Marker element to be used within itemIdentifiers marking the placement of measures + */ + public static final String MEASURE_GROUP = "measureGroup"; + + private final List itemIdentifiers; + private Set totals; + + /** + * Creates new instance + * + * @param itemIdentifiers identifiers referencing attributes from {@link Afm} or {@link Dimension#MEASURE_GROUP} + * @param totals set of totals + */ + @JsonCreator + public Dimension( + @JsonProperty("itemIdentifiers") final List itemIdentifiers, + @JsonProperty("totals") final Set totals) { + this.itemIdentifiers = itemIdentifiers; + this.totals = totals; + } + + /** + * Creates new instance + * + * @param itemIdentifiers identifiers referencing attributes from {@link Afm} or {@link Dimension#MEASURE_GROUP} + */ + public Dimension(final List itemIdentifiers) { + this.itemIdentifiers = itemIdentifiers; + } + + /** + * Creates new instance + * + * @param itemIdentifiers identifiers referencing attributes from {@link Afm} or {@link Dimension#MEASURE_GROUP} + */ + public Dimension(final String... itemIdentifiers) { + this(asList(itemIdentifiers)); + } + + public List getItemIdentifiers() { + return itemIdentifiers; + } + + public Set getTotals() { + return totals; + } + + public void setTotals(final Set totals) { + this.totals = totals; + } + + public Dimension addTotal(final TotalItem total) { + if (totals == null) { + setTotals(new HashSet<>()); + } + totals.add(notNull(total, "total")); + return this; + } + + public Set findTotals(final String attrIdentifier) { + final Predicate filter = t -> notNull(attrIdentifier, "attrIdentifier").equals(t.getAttributeIdentifier()); + + if (totals == null) { + return Collections.emptySet(); + } else { + return totals.stream().filter(filter).collect(Collectors.toSet()); + } + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/Direction.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/Direction.java new file mode 100644 index 000000000..2fa830757 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/Direction.java @@ -0,0 +1,19 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum Direction { + + ASC, DESC; + + @JsonValue + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/LocatorItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/LocatorItem.java new file mode 100644 index 000000000..70654df42 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/LocatorItem.java @@ -0,0 +1,22 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Locator hold information about specific element in path of elements. + * It can be attribute element, metric, etc. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = AttributeLocatorItem.class, name = "attributeLocatorItem"), + @JsonSubTypes.Type(value = MeasureLocatorItem.class, name = "measureLocatorItem"), + @JsonSubTypes.Type(value = TotalLocatorItem.class, name = "totalLocatorItem") +}) +public interface LocatorItem { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/MeasureLocatorItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/MeasureLocatorItem.java new file mode 100644 index 000000000..0ca8ad407 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/MeasureLocatorItem.java @@ -0,0 +1,37 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * Holds metric position + */ +@JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.NAME) +@JsonTypeName("measureLocatorItem") +public class MeasureLocatorItem implements LocatorItem { + private final String measureIdentifier; + + @JsonCreator + public MeasureLocatorItem( + @JsonProperty("measureIdentifier") final String measureIdentifier) { + this.measureIdentifier = measureIdentifier; + } + + public String getMeasureIdentifier() { + return measureIdentifier; + } + + @Override + public String toString() { + return measureIdentifier; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/MeasureSortItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/MeasureSortItem.java new file mode 100644 index 000000000..ea95fef26 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/MeasureSortItem.java @@ -0,0 +1,57 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.util.Arrays.asList; + +/** + * Define metric sort + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("measureSortItem") +public class MeasureSortItem implements SortItem { + private final String direction; + private final List locators; + + @JsonCreator + public MeasureSortItem( + @JsonProperty("direction") final String direction, + @JsonProperty("locators") final List locators) { + this.direction = direction; + this.locators = locators; + } + + public MeasureSortItem(final Direction direction, final List locators) { + this(notNull(direction, "direction").toString(), locators); + } + + public MeasureSortItem(final Direction direction, final LocatorItem... locators) { + this(direction, asList(locators)); + } + + public String getDirection() { + return direction; + } + + public List getLocators() { + return locators; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/ResultSpec.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/ResultSpec.java new file mode 100644 index 000000000..3aebfddff --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/ResultSpec.java @@ -0,0 +1,75 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.executeafm.afm.Afm; + +import java.util.ArrayList; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Represents the result specification of executed {@link Afm} + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ResultSpec { + private List dimensions; + private List sorts; + + @JsonCreator + public ResultSpec( + @JsonProperty("dimensions") final List dimensions, + @JsonProperty("sorts") final List sorts) { + this.dimensions = dimensions; + this.sorts = sorts; + } + + public ResultSpec() { + } + + public List getDimensions() { + return dimensions; + } + + public void setDimensions(final List dimensions) { + this.dimensions = dimensions; + } + + public List getSorts() { + return sorts; + } + + public void setSorts(final List sorts) { + this.sorts = sorts; + } + + public ResultSpec addDimension(final Dimension dimension) { + if (dimensions == null) { + setDimensions(new ArrayList<>()); + } + dimensions.add(notNull(dimension, "dimension")); + return this; + } + + public ResultSpec addSort(final SortItem sort) { + if (sorts == null) { + setSorts(new ArrayList<>()); + } + sorts.add(notNull(sort, "sort")); + return this; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/SortItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/SortItem.java new file mode 100644 index 000000000..d6891c73b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/SortItem.java @@ -0,0 +1,20 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Define sort in report, either attribute (sort headers) or metric (sort data) + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = AttributeSortItem.class, name = "attributeSortItem"), + @JsonSubTypes.Type(value = MeasureSortItem.class, name = "measureSortItem") +}) +public interface SortItem { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/TotalItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/TotalItem.java new file mode 100644 index 000000000..b125547a5 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/TotalItem.java @@ -0,0 +1,92 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.report.Total; + +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Total definition + */ +public class TotalItem { + private final String measureIdentifier; + private final String type; + private final String attributeIdentifier; + + /** + * Total definition + * + * @param measureIdentifier measure on which is total defined + * @param type total type + * @param attributeIdentifier internal attribute identifier in AFM defining total placement + */ + @JsonCreator + public TotalItem( + @JsonProperty("measureIdentifier") final String measureIdentifier, @JsonProperty("type") final String type, + @JsonProperty("attributeIdentifier") final String attributeIdentifier) { + this.measureIdentifier = notEmpty(measureIdentifier, "measureIdentifier"); + this.type = type; + this.attributeIdentifier = notEmpty(attributeIdentifier, "attributeIdentifier"); + } + + public TotalItem(final String measureIdentifier, final Total total, final String attributeIdentifier) { + this(measureIdentifier, notNull(total, "total").toString(), attributeIdentifier); + } + + /** + * total type + * + * @return total type + */ + public String getType() { + return type; + } + + /** + * internal measure identifier in AFM, on which is total defined + * + * @return measure + */ + public String getMeasureIdentifier() { + return measureIdentifier; + } + + /** + * internal attribute identifier in AFM defining total placement + * + * @return identifier (never null) + */ + public String getAttributeIdentifier() { + return attributeIdentifier; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final TotalItem totalItem = (TotalItem) o; + return Objects.equals(measureIdentifier, totalItem.measureIdentifier) && + Objects.equals(type, totalItem.type) && + Objects.equals(attributeIdentifier, totalItem.attributeIdentifier); + } + + @Override + public int hashCode() { + return Objects.hash(measureIdentifier, type, attributeIdentifier); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/TotalLocatorItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/TotalLocatorItem.java new file mode 100644 index 000000000..8929c5f64 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/executeafm/resultspec/TotalLocatorItem.java @@ -0,0 +1,42 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.executeafm.resultspec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Holds total type and attribute to which this total corresponds. + * This enables sorting measure by specific totals. + */ +public class TotalLocatorItem implements LocatorItem { + + private final String attributeIdentifier; + private final String totalType; + + @JsonCreator + public TotalLocatorItem( + @JsonProperty("attributeIdentifier") String attributeIdentifier, + @JsonProperty("totalType") String totalType) { + this.attributeIdentifier = attributeIdentifier; + this.totalType = totalType; + } + + public String getAttributeIdentifier() { + return attributeIdentifier; + } + + public String getTotalType() { + return totalType; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ClientExport.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ClientExport.java new file mode 100644 index 000000000..2258a8f0b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ClientExport.java @@ -0,0 +1,53 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.export; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +@JsonTypeName("clientExport") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ClientExport { + + private static final String DASHBOARD_EXPORT_URI = "/dashboard.html#project=%s&dashboard=%s&tab=%s&export=1"; + + private final String url; + private final String name; + + ClientExport(final String url, final String name) { + this.url = notEmpty(url, "url"); + this.name = notEmpty(name, "name"); + } + + public ClientExport(final String goodDataEndpointUri, final String projectUri, final String dashboardUri, + final String tabId) { + this(notEmpty(goodDataEndpointUri, "goodDataEndpointUri") + + String.format(DASHBOARD_EXPORT_URI, + notNull(projectUri, "projectUri"), + notNull(dashboardUri, "dashboardUri"), + notNull(tabId, "tabId")), + "export.pdf"); + } + + public String getUrl() { + return url; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExecuteReport.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExecuteReport.java new file mode 100644 index 000000000..e5a8bdddb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExecuteReport.java @@ -0,0 +1,44 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.export; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.report.Report; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Report execution request + */ +public class ExecuteReport extends ReportRequest { + + private final String reportUri; + + ExecuteReport(final String reportUri) { + this.reportUri = notNull(reportUri, "reportUri"); + } + + /** + * Create ExecuteReport based on {@link Report} + * + * @param report to create report execution request for + */ + public ExecuteReport(final Report report) { + this(notNull(report, "report").getUri()); + } + + @JsonProperty("report") + public String getReportUri() { + return reportUri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExecuteReportDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExecuteReportDefinition.java new file mode 100644 index 000000000..2efe1ded7 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExecuteReportDefinition.java @@ -0,0 +1,44 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.export; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.report.ReportDefinition; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Report definition execution request + */ +public class ExecuteReportDefinition extends ReportRequest { + + private final String reportDefinitionUri; + + ExecuteReportDefinition(final String reportDefinitionUri) { + this.reportDefinitionUri = notNull(reportDefinitionUri, "reportDefinitionUri"); + } + + /** + * Create ExecuteReportDefinition based on {@link ReportDefinition} + * + * @param reportDefinition to create report definition execution request for + */ + public ExecuteReportDefinition(final ReportDefinition reportDefinition) { + this(notNull(reportDefinition, "reportDefinition").getUri()); + } + + @JsonProperty("reportDefinition") + public String getReportDefinitionUri() { + return reportDefinitionUri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExportFormat.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExportFormat.java new file mode 100644 index 000000000..2351381ca --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ExportFormat.java @@ -0,0 +1,26 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.export; + +import java.util.stream.Stream; + +/** + * Format of exported report + */ +public enum ExportFormat { + + PDF, XLS, PNG, CSV, HTML, XLSX; + + public static String[] arrayToStringArray(final ExportFormat... formats) { + return Stream.of(formats) + .map(ExportFormat::getValue) + .toArray(String[]::new); + } + + public String getValue() { + return name().toLowerCase(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ReportRequest.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ReportRequest.java new file mode 100644 index 000000000..a45308ee8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/export/ReportRequest.java @@ -0,0 +1,25 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.export; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * Report execution request. + * Serialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("report_req") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class ReportRequest { + + public static final String URI = "/gdc/xtab2/executor3"; + +} \ No newline at end of file diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/FeatureFlag.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/FeatureFlag.java new file mode 100644 index 000000000..484ff016a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/FeatureFlag.java @@ -0,0 +1,58 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.featureflag; + +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Feature flag is a boolean flag used for enabling / disabling some specific feature of GoodData platform. + * It can be used in various scopes (per project, per project group, per user, global etc.). + */ +@Deprecated +public class FeatureFlag { + + private final String name; + private final boolean enabled; + + public FeatureFlag(final String name, final boolean enabled) { + this.name = notNull(name, "name"); + this.enabled = enabled; + } + + public String getName() { + return name; + } + + public boolean isEnabled() { + return enabled; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final FeatureFlag that = (FeatureFlag) o; + + if (enabled != that.enabled) return false; + return !(name != null ? !name.equals(that.name) : that.name != null); + + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (enabled ? 1 : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/FeatureFlags.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/FeatureFlags.java new file mode 100644 index 000000000..9d81c3811 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/FeatureFlags.java @@ -0,0 +1,103 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.featureflag; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.util.stream.Collectors.toMap; + +@Deprecated +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("featureFlags") +@JsonIgnoreProperties(ignoreUnknown = true) +public class FeatureFlags implements Iterable { + + public static final String AGGREGATED_FEATURE_FLAGS_URI = "/gdc/internal/projects/{projectId}/featureFlags"; + + private final List featureFlags = new LinkedList<>(); + + /** + * Adds the feature flag of given name and given value. + * + * @param name feature flag name + * @param enabled feature flag value (enabled / disabled) + */ + @JsonAnySetter + public void addFlag(final String name, final boolean enabled) { + notNull(name, "name"); + featureFlags.add(new FeatureFlag(name, enabled)); + } + + /** + * Removes flag of given name. + * + * @param flagName name of the flag to remove + */ + public void removeFlag(final String flagName) { + findFlag(flagName).ifPresent(featureFlags::remove); + } + + /** + * Converts feature flags to map where flags' names are the keys and values are flags' enabled property + * + * @return feature flags as map + */ + @JsonAnyGetter + private Map asMap() { + return featureFlags.stream().collect(toMap(FeatureFlag::getName, FeatureFlag::isEnabled)); + } + + @Override + public Iterator iterator() { + return featureFlags.iterator(); + } + + /** + * Returns true if the feature flag with given name exists and is enabled, false otherwise. + * + * @param flagName the name of feature flag + * @return true if the feature flag with given name exists and is enabled, false otherwise + */ + public boolean isEnabled(final String flagName) { + notEmpty(flagName, "flagName"); + return findFlag(flagName).map(FeatureFlag::isEnabled).orElse(false); + } + + private Optional findFlag(final String flagName) { + return featureFlags.stream().filter(f -> flagName.equalsIgnoreCase(f.getName())).findAny(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final FeatureFlags that = (FeatureFlags) o; + return featureFlags.equals(that.featureFlags); + } + + @Override + public int hashCode() { + return featureFlags.hashCode(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/ProjectFeatureFlag.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/ProjectFeatureFlag.java new file mode 100644 index 000000000..664034e8d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/ProjectFeatureFlag.java @@ -0,0 +1,133 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.featureflag; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Project feature flag is a boolean flag used for enabling / disabling some specific feature of GoodData platform + * on per project basis. + */ +@Deprecated +@JsonTypeName("featureFlag") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ProjectFeatureFlag { + + public static final String PROJECT_FEATURE_FLAG_URI = ProjectFeatureFlags.PROJECT_FEATURE_FLAGS_URI + "/{featureFlag}"; + + private final String name; + @JsonIgnore + private final Links links; + private boolean enabled; + + /** + * Creates new project feature flag which is by default enabled (true). + * + * @param name unique name of feature flag + */ + public ProjectFeatureFlag(String name) { + this(notEmpty(name, "name"), true, null); + } + + /** + * Creates new project feature flag with given value. + * + * @param name unique name of feature flag + * @param enabled true (flag enabled) or false (flag disabled) + */ + public ProjectFeatureFlag(String name, boolean enabled) { + this(notEmpty(name, "name"), enabled, null); + } + + @JsonCreator + ProjectFeatureFlag(@JsonProperty("key") String name, + @JsonProperty("value") boolean enabled, + @JsonProperty("links") Links links) { + this.name = name; + this.enabled = enabled; + this.links = links; + } + + @JsonProperty("key") + public String getName() { + return name; + } + + @JsonProperty("value") + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").getSelf(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final ProjectFeatureFlag that = (ProjectFeatureFlag) o; + + if (enabled != that.enabled) { + return false; + } + return !(name != null ? !name.equals(that.name) : that.name != null); + + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (enabled ? 1 : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + static class Links { + private final String self; + + @JsonCreator + public Links(@JsonProperty("self") String self) { + this.self = self; + } + + public String getSelf() { + return self; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/ProjectFeatureFlags.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/ProjectFeatureFlags.java new file mode 100644 index 000000000..bcd2cd37b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/featureflag/ProjectFeatureFlags.java @@ -0,0 +1,65 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.featureflag; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.project.Project; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +@Deprecated +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("featureFlags") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProjectFeatureFlags implements Iterable { + + public static final String PROJECT_FEATURE_FLAGS_URI = Project.URI + "/projectFeatureFlags"; + + @JsonProperty("items") + private final List items = new LinkedList<>(); + + @JsonCreator + ProjectFeatureFlags(@JsonProperty("items") List items) { + if (items != null && !items.isEmpty()) { + this.items.addAll(items); + } + } + + @Override + public Iterator iterator() { + return items.iterator(); + } + + /** + * Returns true if the project feature flag with given name exists and is enabled, false otherwise. + * + * @param flagName the name of project feature flag + * @return true if the project feature flag with given name exists and is enabled, false otherwise + */ + public boolean isEnabled(final String flagName) { + notEmpty(flagName, "flagName"); + for (final ProjectFeatureFlag flag : items) { + if (flagName.equalsIgnoreCase(flag.getName())) { + return flag.isEnabled(); + } + } + return false; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AboutLinks.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AboutLinks.java new file mode 100644 index 000000000..14e57a9b0 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AboutLinks.java @@ -0,0 +1,116 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.gdc; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; +import java.util.List; + +/** + * Collection of links with "about" metadata. + * Deserialization only. + */ +@JsonTypeName("about") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AboutLinks { + + private final String category; + private final String summary; + private final String instance; + private final List links; + + @JsonCreator + public AboutLinks(@JsonProperty("category") String category, @JsonProperty("summary") String summary, + @JsonProperty("instance") String instance, @JsonProperty("links") List links) { + this.category = category; + this.summary = summary; + this.instance = instance; + this.links = links; + } + + public String getCategory() { + return category; + } + + public String getSummary() { + return summary; + } + + public String getInstance() { + return instance; + } + + public Collection getLinks() { + return links; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * Link with metadata. + * Deserialization only. + */ + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Link { + private final String identifier; + private final String uri; + private final String title; + private final String category; + private final String summary; + + @JsonCreator + public Link(@JsonProperty("identifier") String identifier, @JsonProperty("link") String uri, + @JsonProperty("title") String title, @JsonProperty("category") String category, + @JsonProperty("summary") String summary) { + this.identifier = identifier; + this.uri = uri; + this.title = title; + this.category = category; + this.summary = summary; + } + + public Link(String identifier, String uri, String title) { + this(identifier, uri, title, null, null); + } + + public String getIdentifier() { + return identifier; + } + + public String getUri() { + return uri; + } + + public String getTitle() { + return title; + } + + public String getCategory() { + return category; + } + + public String getSummary() { + return summary; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AbstractMaql.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AbstractMaql.java new file mode 100644 index 000000000..0ebff076d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AbstractMaql.java @@ -0,0 +1,39 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.gdc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Abstract MAQL statement. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("manage") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class AbstractMaql { + + @JsonProperty("maql") + private final String maql; + + public AbstractMaql(String maql) { + this.maql = maql; + } + + public String getMaql() { + return maql; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AsyncTask.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AsyncTask.java new file mode 100644 index 000000000..8fd0f34b9 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/AsyncTask.java @@ -0,0 +1,57 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.gdc; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * Asynchronous task containing link for polling. + * Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("asyncTask") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AsyncTask { + + @JsonProperty + private final Link link; + + @JsonCreator + private AsyncTask(@JsonProperty("link") Link link) { + this.link = link; + } + + public AsyncTask(final String uri) { + this.link = new Link(uri); + } + + @JsonIgnore + public String getUri() { + return link.getPoll(); + } + + private static class Link { + + private final String poll; + + @JsonCreator + private Link(@JsonProperty("poll") String poll) { + this.poll = poll; + } + + public String getPoll() { + return poll; + } + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/LinkEntries.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/LinkEntries.java new file mode 100644 index 000000000..3324c8177 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/LinkEntries.java @@ -0,0 +1,62 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.gdc; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +/** + * Collection of links. Typically used as a result of asynchronous task returning more links. + * Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class LinkEntries { + + private final List entries; + + @JsonCreator + protected LinkEntries(@JsonProperty("entries") List entries) { + this.entries = entries; + } + + protected List getEntries() { + return entries; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + protected static class LinkEntry { + private final String uri; + private final String category; + + @JsonCreator + private LinkEntry(@JsonProperty("link") String uri, @JsonProperty("category") String category) { + this.uri = uri; + this.category = category; + } + + public String getUri() { + return uri; + } + + public String getCategory() { + return category; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/RootLinks.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/RootLinks.java new file mode 100644 index 000000000..fed0c3402 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/RootLinks.java @@ -0,0 +1,240 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.gdc; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.GoodDataException; + +import java.util.List; + +/** + * GoodData API root links (aka "about" or "home"). + * Deserialization only. + */ +public class RootLinks extends AboutLinks { + + /** + * URI of GoodData API root + */ + public static final String URI = "/gdc"; + + @JsonCreator + public RootLinks(@JsonProperty("links") List links) { + super(null, null, null, links); + } + + /** + * Get GoodData API root URI string + * + * @return GoodData API root URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getHomeUri() { + return getLink(LinkCategory.HOME).getUri(); + } + + /** + * Get temporary token generator URI string + * + * @return temporary token generator URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getTokenUri() { + return getLink(LinkCategory.TOKEN).getUri(); + } + + /** + * Get authentication service URI string + * + * @return authentication service URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getLoginUri() { + return getLink(LinkCategory.LOGIN).getUri(); + } + + /** + * Get metadata resources URI string + * + * @return metadata resources URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getMetadataUri() { + return getLink(LinkCategory.METADATA).getUri(); + } + + /** + * Get report execution resource URI string + * + * @return report execution resource URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getXTabUri() { + return getLink(LinkCategory.XTAB).getUri(); + } + + /** + * Get URI string of resource used to determine valid attribute values in the context of a report + * + * @return URI string of resource used to determine valid attribute values in the context of a report + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getAvailableElementsUri() { + return getLink(LinkCategory.AVAILABLE_ELEMENTS).getUri(); + } + + /** + * Get report exporting resource URI string + * + * @return report exporting resource URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getReportExporterUri() { + return getLink(LinkCategory.REPORT_EXPORTER).getUri(); + } + + /** + * Get account manipulation resource URI string + * + * @return account manipulation resource URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getAccountUri() { + return getLink(LinkCategory.ACCOUNT).getUri(); + } + + /** + * Get user and project management resource URI string + * + * @return user and project management resource URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getProjectsUri() { + return getLink(LinkCategory.PROJECTS).getUri(); + } + + /** + * Get miscellaneous tool resource URI string + * + * @return miscellaneous tool resource URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getToolUri() { + return getLink(LinkCategory.TOOL).getUri(); + } + + /** + * Get template resource URI string + * + * @return template resource URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getTemplatesUri() { + return getLink(LinkCategory.TEMPLATES).getUri(); + } + + /** + * Get user data staging area URI string + * + * @return user data staging area URI string + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + public String getUserStagingUri() { + return getLink(LinkCategory.USER_STAGING).getUri(); + } + + /** + * Get link by category + * + * @param category requested link category + * @return link by given category + * @throws GoodDataException in case no link with such category found + */ + @JsonIgnore + private Link getLink(LinkCategory category) { + for (Link link : getLinks()) { + if (category.value.equals(link.getCategory())) { + return link; + } + } + throw new GoodDataException("No link with such category found"); + } + + /** + * GoodData API root link category enum + */ + private enum LinkCategory { + /** + * GoodData API root + */ + HOME("home"), + /** + * Temporary token generator + */ + TOKEN("token"), + /** + * Authentication service + */ + LOGIN("login"), + /** + * Metadata resources + */ + METADATA("md"), + /** + * Report execution resource + */ + XTAB("xtab"), + /** + * Resource used to determine valid attribute values in the context of a report + */ + AVAILABLE_ELEMENTS("availablelements"), + /** + * Report exporting resource + */ + REPORT_EXPORTER("report-exporter"), + /** + * Resource for logged in account manipulation + */ + ACCOUNT("account"), + /** + * Resource for user and project management + */ + PROJECTS("projects"), + /** + * Miscellaneous resources + */ + TOOL("tool"), + /** + * Template resource - for internal use only + */ + TEMPLATES("templates"), + /** + * User data staging area + */ + USER_STAGING("uploads"); + + private final String value; + + LinkCategory(final String value) { + this.value = value; + } + } + +} \ No newline at end of file diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/TaskStatus.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/TaskStatus.java new file mode 100644 index 000000000..b9d70dc1b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/TaskStatus.java @@ -0,0 +1,73 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.gdc; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.gdc.GdcError; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; +import java.util.Collections; + +/** + * Abstract asynchronous task status. + * Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("wTaskStatus") +@JsonIgnoreProperties(ignoreUnknown = true) +public class TaskStatus { + + private static final String OK = "OK"; + private static final String RUNNING = "RUNNING"; + + private final String status; + + private final String pollUri; + + private final Collection messages; + + @JsonCreator + private TaskStatus(@JsonProperty("status") String status, @JsonProperty("poll") String pollUri, + @JsonProperty("messages") Collection messages) { + this.status = status; + this.pollUri = pollUri; + this.messages = messages; + } + + public TaskStatus(final String status, final String pollUri) { + this(status, pollUri, Collections.emptyList()); + } + + public String getStatus() { + return status; + } + + public String getPollUri() { + return pollUri; + } + + public Collection getMessages() { + return messages; + } + + public boolean isSuccess() { + return OK.equals(status); + } + + public boolean isRunning() { + return RUNNING.equals(status); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/UriResponse.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/UriResponse.java new file mode 100644 index 000000000..0aa2201ad --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/gdc/UriResponse.java @@ -0,0 +1,41 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.gdc; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Response containing URI string. + *

+ * Intended for internal library purposes only, not to be used in public API! + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UriResponse implements Serializable { + + private static final long serialVersionUID = 7622971178590505890L; + private final String uri; + + @JsonCreator + public UriResponse(@JsonProperty("uri") String uri) { + this.uri = uri; + } + + public String getUri() { + return uri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/ConfigItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/ConfigItem.java new file mode 100644 index 000000000..aec37382b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/ConfigItem.java @@ -0,0 +1,180 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.hierarchicalconfig; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNullState; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.CLIENT; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.DATA_PRODUCT; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.DOMAIN; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.PROJECT; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.PROJECT_GROUP; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.SEGMENT; + +/** + * Contains information about hierarchical configuration object aka platform setting aka feature flag. + * Purpose of config item is carrying information about some specific feature of Gooddata platform. + */ +@JsonTypeName("settingItem") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ConfigItem { + + public static final String CLIENT_CONFIG_ITEM_URI = CLIENT.getApiUri() + "/{configName}"; + public static final String DATA_PRODUCT_CONFIG_ITEM_URI = DATA_PRODUCT.getApiUri() + "/{configName}"; + public static final String SEGMENT_CONFIG_ITEM_URI = SEGMENT.getApiUri() + "/{configName}"; + public static final String DOMAIN_CONFIG_ITEM_URI = DOMAIN.getApiUri() + "/{configName}"; + public static final String PROJECT_CONFIG_ITEM_URI = PROJECT.getApiUri() + "/{configName}"; + public static final String PROJECT_GROUP_CONFIG_ITEM_URI = PROJECT_GROUP.getApiUri() + "/{configName}"; + + private final String key; + private final String source; + private final Links links; + private String value; + + /** + * Creates new config item with given key/name and value. + * + * @param key unique key/name of config item + * @param value value of config item + */ + public ConfigItem(String key, String value) { + this(notEmpty(key, "key"), notEmpty(value, "value"), null, null); + } + + @JsonCreator + ConfigItem(@JsonProperty("key") String key, @JsonProperty("value") String value, + @JsonProperty("source") String source, @JsonProperty("links") Links links) { + this.key = key; + this.value = value; + this.source = source; + this.links = links; + } + + /** + * Get unique key/name of config item. The same as {@link #getName()}. + */ + @JsonProperty("key") + public String getKey() { + return key; + } + + /** + * Get unique key/name of config item. The same as {@link #getKey()}. + */ + @JsonIgnore + public String getName() { + return key; + } + + @JsonProperty("value") + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @JsonIgnore + public String getSource() { + return source; + } + + @JsonIgnore + public Links getLinks() { + return links; + } + + /** + * Conversion method from String to Boolean type. + * + * @return returns converted value: true or false + */ + @JsonIgnore + public boolean isEnabled() { + return Boolean.parseBoolean(this.value); + } + + public void setEnabled(Boolean value) { + this.value = String.valueOf(value); + } + + /** + * Conversion method from source to SourceType enum. + * + * @return returns SourceType or null, if source is not recognized + */ + @JsonIgnore + public SourceType getSourceType() { + return SourceType.get(source); + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").getSelf(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final ConfigItem that = (ConfigItem) o; + + if (value != null ? !value.equals(that.value) : that.value != null) { + return false; + } + return !(key != null ? !key.equals(that.key) : that.key != null); + + } + + @Override + public int hashCode() { + int result = key != null ? key.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + static class Links { + private final String self; + + @JsonCreator + public Links(@JsonProperty("self") String self) { + this.self = self; + } + + public String getSelf() { + return self; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/ConfigItems.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/ConfigItems.java new file mode 100644 index 000000000..e09b1b0c7 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/ConfigItems.java @@ -0,0 +1,93 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.hierarchicalconfig; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.CLIENT; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.DATA_PRODUCT; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.DOMAIN; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.PROJECT; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.PROJECT_GROUP; +import static com.gooddata.sdk.model.hierarchicalconfig.SourceType.SEGMENT; + +/** + * Contains collection of config items aka feature flags aka hierarchical config. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("settings") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ConfigItems implements Iterable { + + public static final String CLIENT_CONFIG_ITEMS_URI = CLIENT.getApiUri(); + public static final String DATA_PRODUCT_CONFIG_ITEMS_URI = DATA_PRODUCT.getApiUri(); + public static final String SEGMENT_CONFIG_ITEMS_URI = SEGMENT.getApiUri(); + public static final String DOMAIN_CONFIG_ITEMS_URI = DOMAIN.getApiUri(); + public static final String PROJECT_CONFIG_ITEMS_URI = PROJECT.getApiUri(); + public static final String PROJECT_GROUP_CONFIG_ITEMS_URI = PROJECT_GROUP.getApiUri(); + + @JsonProperty("items") + private final List items = new ArrayList<>(); + + @JsonCreator + public ConfigItems(@JsonProperty("items") List items) { + if (items != null && !items.isEmpty()) { + this.items.addAll(items); + } + } + + @Override + public Iterator iterator() { + return items.iterator(); + } + + /** + * Returns value if config item exists, otherwise returns null. + * + * @param configName the name/key of the config item + * @return value if config item with given name/key exists, null otherwise + */ + public String getValue(final String configName) { + notEmpty(configName, "configName"); + for (final ConfigItem item : items) { + if (configName.equalsIgnoreCase(item.getKey())) { + return item.getValue(); + } + } + return null; + } + + /** + * Returns true if the config item with given name/key exists and is enabled, false otherwise. + * + * @param configName the name/key of config item + * @return true if the config item with given name/key exists and is enabled, false otherwise + */ + public boolean isEnabled(final String configName) { + notEmpty(configName, "configName"); + for (final ConfigItem item : items) { + if (configName.equalsIgnoreCase(item.getKey())) { + return Boolean.parseBoolean(item.getValue()); + } + } + return false; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/SourceType.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/SourceType.java new file mode 100644 index 000000000..4a3a1b0eb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/hierarchicalconfig/SourceType.java @@ -0,0 +1,88 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.hierarchicalconfig; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.Map; + +/** + * Source type defines source of config item and its api uri in case it is manageable. + * For example, the project can inherit config from the domain, but if the config is defined for both: domain and project, + * then config from project takes precedence. + */ +public enum SourceType { + + @JsonProperty("catalog") + CATALOG("catalog", null), + @JsonProperty("client") + CLIENT("client", "/gdc/domains/{domainName}/dataproducts/{dataProductId}/clients/{clientId}/config"), + @JsonProperty("data_product") + DATA_PRODUCT("data_product", "/gdc/domains/{domainName}/dataproducts/{dataProductId}/config"), + @JsonProperty("segment") + SEGMENT("segment", "/gdc/domains/{domainName}/dataproducts/{dataProductId}/segments/{segmentId}/config"), + @JsonProperty("datawarehouse") + DATAWAREHOUSE("datawarehouse", null), + @JsonProperty("domain") + DOMAIN("domain", "/gdc/domains/{domainName}/config"), + @JsonProperty("project") + PROJECT("project", "/gdc/projects/{pid}/config"), + @JsonProperty("project_group") + PROJECT_GROUP("project_group", "/gdc/projectGroups/{projectGroup}/config"), + @JsonProperty("user") + USER("user", null); + + private static final Map lookup = new HashMap<>(); + + static { + for (SourceType c : SourceType.values()) { + lookup.put(c.name, c); + } + } + + /** + * Name of the source type of config item + */ + private final String name; + + /** + * Api uri in case that config item is manageable + */ + private final String apiUri; + + /** + * @param name source of config item + * @param apiUri api uri in case that config item is manageable + */ + SourceType(String name, String apiUri) { + this.name = name; + this.apiUri = apiUri; + } + + /** + * Get source type by string. + * + * @param source name of the source + * @return Source type enum instance according to the given name + */ + public static SourceType get(String source) { + return lookup.get(source); + } + + public String getName() { + return name; + } + + public String getApiUri() { + return apiUri; + } + + @Override + public String toString() { + return this.name; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntities.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntities.java new file mode 100644 index 000000000..30dd48279 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntities.java @@ -0,0 +1,63 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.lcm; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.collections.Page; +import com.gooddata.sdk.common.collections.PageDeserializer; +import com.gooddata.sdk.common.collections.PageSerializer; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; + +/** + * List of {@link LcmEntity}. + */ +@JsonDeserialize(using = LcmEntities.Deserializer.class) +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(LcmEntities.ROOT_NODE) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSerialize(using = LcmEntities.Serializer.class) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class LcmEntities extends Page { + + public static final String URI = "/gdc/account/profile/{profileId}/lcmEntities"; + + static final String ROOT_NODE = "lcmEntities"; + + public LcmEntities(final List items, final Paging paging, final Map links) { + super(items, paging, links); + } + + LcmEntities(final LcmEntity... lcmEntities) { + this(asList(lcmEntities), null, null); + } + + static class Serializer extends PageSerializer { + public Serializer() { + super(ROOT_NODE); + } + } + + static class Deserializer extends PageDeserializer { + protected Deserializer() { + super(LcmEntity.class); + } + + @Override + protected LcmEntities createPage(final List items, final Paging paging, final Map links) { + return new LcmEntities(items, paging, links); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntity.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntity.java new file mode 100644 index 000000000..17f060ddb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntity.java @@ -0,0 +1,146 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.lcm; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.project.Project; + +import java.util.Map; +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.common.util.Validate.notNullState; +import static com.gooddata.sdk.model.lcm.LcmEntity.LinkCategory.CLIENT; +import static com.gooddata.sdk.model.lcm.LcmEntity.LinkCategory.DATA_PRODUCT; +import static com.gooddata.sdk.model.lcm.LcmEntity.LinkCategory.PROJECT; +import static com.gooddata.sdk.model.lcm.LcmEntity.LinkCategory.SEGMENT; + +/** + * Single Life Cycle Management entity representing the relation between {@link Project}, + * Client, Segment and DataProduct. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class LcmEntity { + + private final String projectId; + private final String projectTitle; + private final String clientId; + private final String segmentId; + private final String dataProductId; + private final Map links; + + /** + * Creates new instance of given project id and title + * + * @param projectId id of the project + * @param projectTitle title of the project + * @param links links + */ + public LcmEntity(final String projectId, final String projectTitle, final Map links) { + this(projectId, projectTitle, null, null, null, links); + } + + @JsonCreator + public LcmEntity(@JsonProperty("projectId") final String projectId, + @JsonProperty("projectTitle") final String projectTitle, + @JsonProperty("clientId") final String clientId, + @JsonProperty("segmentId") final String segmentId, + @JsonProperty("dataProductId") final String dataProductId, + @JsonProperty("links") final Map links) { + this.projectId = notEmpty(projectId, "projectId"); + this.projectTitle = notNull(projectTitle, "projectTitle"); + this.clientId = clientId; + this.segmentId = segmentId; + this.dataProductId = dataProductId; + this.links = notNull(links, "links"); + } + + public String getProjectId() { + return projectId; + } + + public String getProjectTitle() { + return projectTitle; + } + + public String getClientId() { + return clientId; + } + + public String getSegmentId() { + return segmentId; + } + + public String getDataProductId() { + return dataProductId; + } + + public Map getLinks() { + return links; + } + + @JsonIgnore + public String getProjectUri() { + return getLink(PROJECT); + } + + @JsonIgnore + public String getClientUri() { + return getLink(CLIENT); + } + + @JsonIgnore + public String getSegmentUri() { + return getLink(SEGMENT); + } + + @JsonIgnore + public String getDataProductUri() { + return getLink(DATA_PRODUCT); + } + + private String getLink(final String link) { + return notNullState(links, "links").get(link); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final LcmEntity lcmEntity = (LcmEntity) o; + return Objects.equals(projectId, lcmEntity.projectId) && + Objects.equals(projectTitle, lcmEntity.projectTitle) && + Objects.equals(clientId, lcmEntity.clientId) && + Objects.equals(segmentId, lcmEntity.segmentId) && + Objects.equals(dataProductId, lcmEntity.dataProductId) && + Objects.equals(links, lcmEntity.links); + } + + @Override + public int hashCode() { + + return Objects.hash(projectId, projectTitle, clientId, segmentId, dataProductId, links); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + public static class LinkCategory { + public static final String PROJECT = "project"; + public static final String CLIENT = "client"; + public static final String SEGMENT = "segment"; + public static final String DATA_PRODUCT = "dataProduct"; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntityFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntityFilter.java new file mode 100644 index 000000000..3d04a01ac --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/lcm/LcmEntityFilter.java @@ -0,0 +1,103 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.lcm; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonList; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +/** + * Specification of the filter on {@link LcmEntities}. + */ +public class LcmEntityFilter { + + private static final String DATA_PRODUCT = "dataProduct"; + private static final String SEGMENT = "segment"; + private static final String CLIENT = "client"; + + private String dataProduct; + private String segment; + private String client; + + /** + * Creates new (empty) filter. + */ + public LcmEntityFilter() { + } + + /** + * Adds given data product to this filter. + * + * @param dataProduct data product id - must not be empty. + * @return this filter + */ + public LcmEntityFilter withDataProduct(final String dataProduct) { + if (isNotBlank(dataProduct)) { + this.dataProduct = dataProduct; + } + return this; + } + + /** + * Adds given segment to this filter. + * + * @param segment segment id - must not be empty. + * @return this filter + */ + public LcmEntityFilter withSegment(final String segment) { + if (isNotBlank(segment)) { + this.segment = segment; + } + return this; + } + + /** + * Adds given client to this filter. + * + * @param client client id - must not be empty. + * @return this filter + */ + public LcmEntityFilter withClient(final String client) { + if (isNotBlank(client)) { + this.client = client; + } + return this; + } + + public String getDataProduct() { + return dataProduct; + } + + public String getSegment() { + return segment; + } + + public String getClient() { + return client; + } + + /** + * This filter in the form of query parameters map. + * + * @return filter as query params map + */ + public Map> asQueryParams() { + final Map> params = new LinkedHashMap<>(); + if (dataProduct != null) { + params.put(DATA_PRODUCT, singletonList(dataProduct)); + } + if (segment != null) { + params.put(SEGMENT, singletonList(segment)); + } + if (client != null) { + params.put(CLIENT, singletonList(client)); + } + return params; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AbstractObj.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AbstractObj.java new file mode 100644 index 000000000..44e9bde7d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AbstractObj.java @@ -0,0 +1,194 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.util.UriHelper; + +import java.io.Serializable; +import java.time.ZonedDateTime; +import java.util.Set; + +import static com.gooddata.sdk.common.util.Validate.noNullElements; + +/** + * Metadata object (common part) + */ +public abstract class AbstractObj implements Serializable { + + private static final long serialVersionUID = 2910760851810495274L; + + @JsonProperty("meta") + protected final Meta meta; + + protected AbstractObj(@JsonProperty("meta") Meta meta) { + this.meta = meta; + } + + /** + * Get list of URIs of the given {@link Obj}s + * + * @param objs metadata objects + * @param Obj type + * @return list of URIs + */ + @SafeVarargs + protected static String[] uris(T... objs) { + noNullElements(objs, "objs"); + final String[] uris = new String[objs.length]; + for (int i = 0; i < objs.length; i++) { + uris[i] = objs[i].getUri(); + } + return uris; + } + + /** + * Returns internally generated ID of the object (that's part of the object URI). + * + * @return internal ID of the object + */ + @JsonIgnore + public String getId() { + return UriHelper.getLastUriPart(getUri()); + } + + @JsonIgnore + public String getAuthor() { + return meta.getAuthor(); + } + + @JsonIgnore + public String getContributor() { + return meta.getContributor(); + } + + @JsonIgnore + public ZonedDateTime getCreated() { + return meta.getCreated(); + } + + @JsonIgnore + public String getSummary() { + return meta.getSummary(); + } + + public void setSummary(String summary) { + meta.setSummary(summary); + } + + @JsonIgnore + public String getTitle() { + return meta.getTitle(); + } + + public void setTitle(String title) { + meta.setTitle(title); + } + + @JsonIgnore + public ZonedDateTime getUpdated() { + return meta.getUpdated(); + } + + @JsonIgnore + public String getCategory() { + return meta.getCategory(); + } + + public void setCategory(String category) { + meta.setCategory(category); + } + + @JsonIgnore + public Set getTags() { + return meta.getTags(); + } + + public void setTags(Set tags) { + meta.setTags(tags); + } + + @JsonIgnore + public String getUri() { + return meta.getUri(); + } + + @JsonIgnore + public boolean isDeprecated() { + return Boolean.TRUE.equals(meta.isDeprecated()); + } + + public void setDeprecated(Boolean deprecated) { + meta.setDeprecated(deprecated); + } + + /** + * Returns user-specified identifier of the object. + * + * @return user-specified object identifier + */ + @JsonIgnore + public String getIdentifier() { + return meta.getIdentifier(); + } + + public void setIdentifier(String identifier) { + meta.setIdentifier(identifier); + } + + @JsonIgnore + public boolean isLocked() { + return Boolean.TRUE.equals(meta.isLocked()); + } + + public void setLocked(Boolean locked) { + meta.setLocked(locked); + } + + @JsonIgnore + public boolean isUnlisted() { + return Boolean.TRUE.equals(meta.isUnlisted()); + } + + public void setUnlisted(Boolean unlisted) { + meta.setUnlisted(unlisted); + } + + @JsonIgnore + public boolean isProduction() { + return Boolean.TRUE.equals(meta.isProduction()); + } + + public void setProduction(Boolean production) { + meta.setProduction(production); + } + + @JsonIgnore + public boolean isSharedWithSomeone() { + return Boolean.TRUE.equals(meta.isSharedWithSomeone()); + } + + public void setSharedWithSomeone(Boolean sharedWithSomeone) { + meta.setSharedWithSomeone(sharedWithSomeone); + } + + @JsonIgnore + public Set getFlags() { + return meta.getFlags(); + } + + public void setFlags(final Set flags) { + meta.setFlags(flags); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Attachment.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Attachment.java new file mode 100644 index 000000000..2dba335f2 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Attachment.java @@ -0,0 +1,61 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Common ancestor to {@link ScheduledMail} attachments. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSubTypes({ + @JsonSubTypes.Type(name = "dashboardAttachment", value = DashboardAttachment.class), + @JsonSubTypes.Type(name = "reportAttachment", value = ReportAttachment.class), +}) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Attachment implements Serializable { + + private static final long serialVersionUID = -3210630720136793710L; + private final String uri; + + @JsonCreator + protected Attachment(@JsonProperty("uri") String uri) { + this.uri = uri; + } + + public String getUri() { + return uri; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Attachment that = (Attachment) o; + + return !(uri != null ? !uri.equals(that.uri) : that.uri != null); + } + + @Override + public int hashCode() { + return uri != null ? uri.hashCode() : 0; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Attribute.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Attribute.java new file mode 100644 index 000000000..d19b54908 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Attribute.java @@ -0,0 +1,36 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import java.util.Collections; + +import static java.util.Arrays.asList; + +/** + * Attribute of GoodData project dataset + */ +@JsonTypeName("attribute") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Attribute extends NestedAttribute implements Queryable, Updatable { + + @JsonCreator + private Attribute(@JsonProperty("meta") Meta meta, @JsonProperty("content") NestedAttribute.Content content) { + super(meta, content); + } + + /* Just for serialization test */ + Attribute(String title, Key primaryKey, Key foreignKey) { + this(new Meta(title), new Content(asList(primaryKey), asList(foreignKey), Collections.emptyList(), null, null, null, null, null, + null, null, null, null, null, null)); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeDisplayForm.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeDisplayForm.java new file mode 100644 index 000000000..d86dcd7e6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeDisplayForm.java @@ -0,0 +1,80 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.BooleanIntegerSerializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Display form of attribute + */ +@JsonTypeName("attributeDisplayForm") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AttributeDisplayForm extends DisplayForm implements Updatable { + + private static final long serialVersionUID = -7903851496647992573L; + + @JsonProperty("content") + protected final AttributeContent attributeContent; + + @JsonCreator + private AttributeDisplayForm(@JsonProperty("meta") Meta meta, @JsonProperty("content") AttributeContent content, + @JsonProperty("links") Links links) { + super(meta, content, links); + this.attributeContent = content; + } + + /* Just for serialization test */ + AttributeDisplayForm(String title, String formOf, String expression, boolean isDefault, String ldmExpression, String type, String elements) { + this(new Meta(title), new AttributeContent(formOf, expression, isDefault, ldmExpression, type), new Links(elements)); + } + + @JsonIgnore + public boolean isDefault() { + return Boolean.TRUE.equals(attributeContent.isDefault()); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class AttributeContent extends DisplayForm.Content { + + private static final long serialVersionUID = -8502672468934478137L; + private final boolean isDefault; + + private AttributeContent(@JsonProperty("formOf") String formOf, @JsonProperty("expression") String expression, + @JsonProperty("default") @JsonDeserialize(using = BooleanDeserializer.class) Boolean isDefault, + @JsonProperty("ldmexpression") String ldmExpression, + @JsonProperty("type") String type) { + super(formOf, expression, ldmExpression, type); + this.isDefault = isDefault; + } + + @JsonProperty("default") + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean isDefault() { + return isDefault; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeElement.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeElement.java new file mode 100644 index 000000000..ffd105b3e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeElement.java @@ -0,0 +1,60 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Represent one element of attribute values + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AttributeElement { + + private final String uri; + private final String title; + + @JsonCreator + private AttributeElement(@JsonProperty("uri") String uri, @JsonProperty("title") String title) { + this.uri = uri; + this.title = title; + } + + public String getUri() { + return uri; + } + + public String getTitle() { + return title; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final AttributeElement that = (AttributeElement) o; + + if (uri != null ? !uri.equals(that.uri) : that.uri != null) return false; + return title != null ? title.equals(that.title) : that.title == null; + } + + @Override + public int hashCode() { + int result = uri != null ? uri.hashCode() : 0; + result = 31 * result + (title != null ? title.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeElements.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeElements.java new file mode 100644 index 000000000..025b141de --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeElements.java @@ -0,0 +1,65 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Represents elements of attribute + */ +@JsonTypeName("attributeElements") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AttributeElements { + + static final String URI = Obj.OBJ_URI + "/elements"; + + private final List elements; + + @JsonCreator + AttributeElements(@JsonProperty("elements") List elements) { + notNull(elements, "elements"); + this.elements = elements; + } + + public List getElements() { + return elements; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + AttributeElements that = (AttributeElements) o; + + return !(elements != null ? !elements.equals(that.elements) : that.elements != null); + + } + + @Override + public int hashCode() { + return elements != null ? elements.hashCode() : 0; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeSort.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeSort.java new file mode 100644 index 000000000..a4d7d8838 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/AttributeSort.java @@ -0,0 +1,92 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.IOException; +import java.io.Serializable; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Internal representation of attribute sort field. Which can be either plain text value or structure pointing to display form. + */ +@JsonDeserialize(using = AttributeSort.Deserializer.class) +@JsonSerialize(using = AttributeSort.Serializer.class) +class AttributeSort implements Serializable { + + static final String PK = "pk"; + static final String BY_USED_DF = "byUsedDF"; + private static final long serialVersionUID = -7415504020870223701L; + private final String value; + private final boolean linkType; + + AttributeSort(String value) { + this(value, false); + } + + AttributeSort(String value, boolean linkType) { + this.value = notEmpty(value, "value"); + this.linkType = linkType; + } + + String getValue() { + return value; + } + + boolean isLinkType() { + return linkType; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + static class Deserializer extends JsonDeserializer { + + @Override + public AttributeSort deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + final JsonNode root = p.readValueAsTree(); + notNull(root, "jsonNode"); + if (root.isTextual()) { + return new AttributeSort(root.textValue()); + } else if (root.isObject()) { + return new AttributeSort(root.findValue("uri").textValue(), true); + } else { + return (AttributeSort) ctxt.handleUnexpectedToken(AttributeSort.class, root.asToken(), p, + "Only textual or object node expected but %s node found", root.getNodeType().name()); + } + } + } + + static class Serializer extends JsonSerializer { + @Override + public void serialize(AttributeSort value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value.linkType) { + gen.writeStartObject(); + gen.writeFieldName("df"); + gen.writeStartObject(); + gen.writeStringField("uri", value.getValue()); + gen.writeEndObject(); + gen.writeEndObject(); + } else { + gen.writeString(value.getValue()); + } + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/BulkGet.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/BulkGet.java new file mode 100644 index 000000000..7ca9401fc --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/BulkGet.java @@ -0,0 +1,44 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +/** + * Metadata bulk get result + */ +@JsonTypeName("objects") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BulkGet { + + public static final String URI = "/gdc/md/{projectId}/objects/get"; + + private final Collection items; + + @JsonCreator + BulkGet(@JsonProperty("items") Collection items) { + this.items = items; + } + + public Collection getItems() { + return items; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/BulkGetUris.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/BulkGetUris.java new file mode 100644 index 000000000..17086a66b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/BulkGetUris.java @@ -0,0 +1,58 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Metadata bulk get request body representation. + * Serialization only. + */ +@JsonTypeName("get") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public class BulkGetUris { + + private final Collection items; + + public BulkGetUris(Collection items) { + notNull(items, "items"); + this.items = items; + } + + @JsonProperty("items") + public Collection getItems() { + return items; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BulkGetUris that = (BulkGetUris) o; + + if (!items.equals(that.items)) return false; + + return true; + } + + @Override + public int hashCode() { + return items.hashCode(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Column.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Column.java new file mode 100644 index 000000000..ced161e41 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Column.java @@ -0,0 +1,132 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Represents physical data model column. Doesn't implement all fields right now. + * Deserialization only. + */ +@JsonTypeName("column") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public class Column extends AbstractObj implements Queryable { + + public static final String TYPE_PK = "pk"; + public static final String TYPE_INPUT_PK = "inputpk"; + public static final String TYPE_FK = "fk"; + public static final String TYPE_FACT = "fact"; + public static final String TYPE_DISPLAY_FORM = "displayForm"; + private static final long serialVersionUID = 8235010456787885263L; + private final Content content; + + private Column(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + /** + * @return uri of physical {@link Table} + */ + public String getTableUri() { + return content.getTable(); + } + + /** + * @return type of column, one of pk,inputpk,fk,fact,displayForm or null + */ + public String getType() { + return content.getColumnType(); + } + + /** + * @return name of the column in DB + */ + public String getDBName() { + return content.getColumnDBName(); + } + + /** + * @return true when type is pk, false otherwise + */ + public boolean isPk() { + return TYPE_PK.equals(getType()); + } + + /** + * @return true when type is inputpk, false otherwise + */ + public boolean isInputPk() { + return TYPE_INPUT_PK.equals(getType()); + } + + /** + * @return true when type is fk, false otherwise + */ + public boolean isFk() { + return TYPE_FK.equals(getType()); + } + + /** + * @return true when type is fact, false otherwise + */ + public boolean isFact() { + return TYPE_FACT.equals(getType()); + } + + /** + * @return true when type is pk, false otherwise + */ + public boolean isDisplayForm() { + return TYPE_DISPLAY_FORM.equals(getType()); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Content implements Serializable { + + private static final long serialVersionUID = -8830741484051984963L; + private final String table; + private final String columnDBName; + private final String columnType; + + @JsonCreator + private Content(@JsonProperty("table") String table, @JsonProperty("columnDBName") String columnDBName, + @JsonProperty("columnType") String columnType) { + this.table = table; + this.columnDBName = columnDBName; + this.columnType = columnType; + } + + public String getTable() { + return table; + } + + public String getColumnDBName() { + return columnDBName; + } + + public String getColumnType() { + return columnType; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DashboardAttachment.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DashboardAttachment.java new file mode 100644 index 000000000..49eb19acd --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DashboardAttachment.java @@ -0,0 +1,77 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Arrays; +import java.util.Collection; + +/** + * Attachment to {@link ScheduledMail} represents dashboard-related information for the schedule. + */ +public class DashboardAttachment extends Attachment { + + private final Integer allTabs; + private final Collection tabs; + private final String executionContext; + + @JsonCreator + protected DashboardAttachment( + @JsonProperty("uri") String uri, + @JsonProperty("allTabs") Integer allTabs, + @JsonProperty("executionContext") String executionContext, + @JsonProperty("tabs") String... tabs + ) { + super(uri); + this.allTabs = allTabs; + this.tabs = Arrays.asList(tabs); + this.executionContext = executionContext; + } + + public Integer getAllTabs() { + return allTabs; + } + + public Collection getTabs() { + return tabs; + } + + public String getExecutionContext() { + return executionContext; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + final DashboardAttachment that = (DashboardAttachment) o; + + if (getUri() != null ? !getUri().equals(that.getUri()) : that.getUri() != null) return false; + if (allTabs != null ? !allTabs.equals(that.allTabs) : that.allTabs != null) return false; + if (tabs != null ? !tabs.equals(that.tabs) : that.tabs != null) return false; + return executionContext != null ? executionContext.equals(that.executionContext) : that.executionContext == null; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (getUri() != null ? getUri().hashCode() : 0); + result = 31 * result + (allTabs != null ? allTabs.hashCode() : 0); + result = 31 * result + (tabs != null ? tabs.hashCode() : 0); + result = 31 * result + (executionContext != null ? executionContext.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return new GoodDataToStringBuilder(this).append("uri", getUri()).toString(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DataLoadingColumn.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DataLoadingColumn.java new file mode 100644 index 000000000..55f0421e6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DataLoadingColumn.java @@ -0,0 +1,195 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.gdc.UriResponse; + +import java.io.Serializable; + +/** + * Represents datasets' loading column. + * Deserialization only. + */ +@JsonTypeName("dataLoadingColumn") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DataLoadingColumn extends AbstractObj implements Queryable { + + private static final long serialVersionUID = -1280718594585386535L; + private static final String INT = "INT"; + private static final String VARCHAR = "VARCHAR"; + + private final Content content; + + @JsonCreator + private DataLoadingColumn(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + public String getColumnUri() { + return content.getColumnUri().getUri(); + } + + public String getName() { + return content.getColumnName(); + } + + public String getType() { + return content.getColumnType(); + } + + /** + * @return true when the type is not null and equal to VARCHAR, false otherwise + */ + public boolean hasTypeVarchar() { + return VARCHAR.equals(getType()); + } + + /** + * @return true when the type is not null and equal to INT, false otherwise + */ + public boolean hasTypeInt() { + return INT.equals(getType()); + } + + public Integer getLength() { + return content.getColumnLength(); + } + + public Integer getPrecision() { + return content.getColumnPrecision(); + } + + public boolean isUnique() { + return content.isColumnUnique(); + } + + public boolean isNullable() { + return content.isColumnNull(); + } + + public String getSynchronizeType() { + return content.getColumnSynchronize() != null ? content.getColumnSynchronize().getType() : null; + } + + public Integer getSynchronizeLength() { + return content.getColumnSynchronize() != null ? content.getColumnSynchronize().getLength() : null; + } + + public Integer getSynchronizePrecision() { + return content.getColumnSynchronize() != null ? content.getColumnSynchronize().getPrecision() : null; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + private static class Content implements Serializable { + + private static final long serialVersionUID = 3821238884831793602L; + private final UriResponse columnUri; + private final String columnName; + private final String columnType; + private final Integer columnLength; + private final Integer columnPrecision; + private final boolean columnUnique; + private final boolean columnNull; + private final ColumnSynchronize columnSynchronize; + + private Content(@JsonProperty("column") UriResponse columnUri, @JsonProperty("columnName") String columnName, @JsonProperty("type") String columnType, + @JsonProperty("length") Integer columnLength, @JsonProperty("precision") Integer columnPrecision, + @JsonProperty("columnUnique") @JsonDeserialize(using = BooleanDeserializer.class) boolean columnUnique, + @JsonProperty("columnNull") @JsonDeserialize(using = BooleanDeserializer.class) boolean columnNull, + @JsonProperty("columnSynchronize") ColumnSynchronize columnSynchronize) { + this.columnUri = columnUri; + this.columnName = columnName; + this.columnType = columnType; + this.columnLength = columnLength; + this.columnPrecision = columnPrecision; + this.columnUnique = columnUnique; + this.columnNull = columnNull; + this.columnSynchronize = columnSynchronize; + } + + public UriResponse getColumnUri() { + return columnUri; + } + + public String getColumnName() { + return columnName; + } + + public String getColumnType() { + return columnType; + } + + public Integer getColumnLength() { + return columnLength; + } + + public Integer getColumnPrecision() { + return columnPrecision; + } + + public boolean isColumnUnique() { + return columnUnique; + } + + public boolean isColumnNull() { + return columnNull; + } + + public ColumnSynchronize getColumnSynchronize() { + return columnSynchronize; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + + private static class ColumnSynchronize implements Serializable { + private final String type; + private final Integer length; + private final Integer precision; + + @JsonCreator + private ColumnSynchronize(@JsonProperty("columnType") String type, @JsonProperty("columnLength") Integer length, + @JsonProperty("columnPrecision") Integer precision) { + this.type = type; + this.length = length; + this.precision = precision; + } + + public String getType() { + return type; + } + + public Integer getLength() { + return length; + } + + public Integer getPrecision() { + return precision; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Dataset.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Dataset.java new file mode 100644 index 000000000..d320b2c66 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Dataset.java @@ -0,0 +1,161 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.BooleanStringSerializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Represents metadata dataset + */ +@JsonTypeName("dataSet") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Dataset extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = -6510493111358411706L; + private static final String DATA_UPLOADS_LINK = "dataUploads"; + private static final String UPLOAD_CONFIGURATION_LINK = "uploadConfiguration"; + + @JsonProperty("content") + private final Content content; + + @JsonProperty("links") + private final Map links; + + @JsonCreator + private Dataset(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content, + @JsonProperty("links") Map links) { + super(meta); + this.content = content; + this.links = links; + } + + /* Just for serialization test */ + Dataset(String title) { + this(new Meta(title), new Content(Collections.emptyList(), null, Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), false), null); + } + + @JsonIgnore + public List getTies() { + return content.getTies(); + } + + @JsonIgnore + public String getMode() { + return content.getMode(); + } + + @JsonIgnore + public List getFacts() { + return content.getFacts(); + } + + @JsonIgnore + public List getDataLoadingColumns() { + return content.getDataLoadingColumns(); + } + + @JsonIgnore + public List getAttributes() { + return content.getAttributes(); + } + + @JsonIgnore + public boolean hasUploadConfiguration() { + return Boolean.TRUE.equals(content.hasUploadConfiguration()); + } + + @JsonIgnore + public String getDataUploadsUri() { + return notNullState(links, "links").get(DATA_UPLOADS_LINK); + } + + @JsonIgnore + public String getUploadConfigurationUri() { + return notNullState(links, "links").get(UPLOAD_CONFIGURATION_LINK); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Content implements Serializable { + + private static final long serialVersionUID = -8819963869909114313L; + private final List ties; + private final String mode; + private final List facts; + private final List dataLoadingColumns; + private final List attributes; + private final Boolean hasUploadConfiguration; + + @JsonCreator + private Content(@JsonProperty("ties") List ties, + @JsonProperty("mode") String mode, + @JsonProperty("facts") List facts, + @JsonProperty("dataLoadingColumns") List dataLoadingColumns, + @JsonProperty("attributes") List attributes, + @JsonProperty("hasUploadConfiguration") @JsonDeserialize(using = BooleanDeserializer.class) Boolean hasUploadConfiguration) { + this.ties = ties; + this.mode = mode; + this.facts = facts; + this.dataLoadingColumns = dataLoadingColumns; + this.attributes = attributes; + this.hasUploadConfiguration = hasUploadConfiguration; + } + + public List getTies() { + return ties; + } + + public String getMode() { + return mode; + } + + public List getFacts() { + return facts; + } + + public List getDataLoadingColumns() { + return dataLoadingColumns; + } + + public List getAttributes() { + return attributes; + } + + @JsonProperty("hasUploadConfiguration") + @JsonSerialize(using = BooleanStringSerializer.class) + public Boolean hasUploadConfiguration() { + return hasUploadConfiguration; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Dimension.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Dimension.java new file mode 100644 index 000000000..a8331fee1 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Dimension.java @@ -0,0 +1,75 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; + +/** + * Represents metadata dimension. + */ +@JsonTypeName("dimension") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Dimension extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = -6667238416764957848L; + + @JsonProperty("content") + private final Content content; + + @JsonCreator + private Dimension(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + /* for serialization test */ + Dimension(String title) { + this(new Meta(title), new Content(Collections.emptyList())); + } + + @JsonIgnore + public Collection getAttributes() { + return content.getAttributes(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + private static class Content implements Serializable { + + private static final long serialVersionUID = -6382409825359596163L; + + @JsonProperty("attributes") + private final Collection attributes; + + @JsonCreator + public Content(@JsonProperty("attributes") Collection attributes) { + this.attributes = attributes; + } + + public Collection getAttributes() { + return attributes; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DisplayForm.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DisplayForm.java new file mode 100644 index 000000000..06c314136 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/DisplayForm.java @@ -0,0 +1,133 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Display form + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DisplayForm extends AbstractObj { + + private static final long serialVersionUID = 8719802319193893780L; + + @JsonProperty("content") + protected final Content content; + + @JsonProperty("links") + private final Links links; + + @JsonCreator + protected DisplayForm(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content, + @JsonProperty("links") Links links) { + super(meta); + this.content = content; + this.links = links; + } + + @JsonIgnore + public String getFormOf() { + return content.getFormOf(); + } + + @JsonIgnore + public String getExpression() { + return content.getExpression(); + } + + @JsonIgnore + public String getLdmExpression() { + return content.getLdmExpression(); + } + + @JsonIgnore + public String getType() { + return content.getType(); + } + + @JsonIgnore + public String getElementsUri() { + return links.getElements(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + protected static class Content implements Serializable { + + private static final long serialVersionUID = 6865880678569437635L; + private final String formOf; + private final String expression; + private final String ldmExpression; + private final String type; + + @JsonCreator + public Content(@JsonProperty("formOf") String formOf, @JsonProperty("expression") String expression, + @JsonProperty("ldmexpression") String ldmExpression, @JsonProperty("type") String type) { + this.formOf = formOf; + this.expression = expression; + this.ldmExpression = ldmExpression; + this.type = type; + } + + public String getFormOf() { + return formOf; + } + + public String getExpression() { + return expression; + } + + @JsonProperty("ldmexpression") + public String getLdmExpression() { + return ldmExpression; + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + protected static class Links implements Serializable { + + private static final long serialVersionUID = 1704085675250093860L; + private final String elements; + + @JsonCreator + protected Links(@JsonProperty("elements") String elements) { + this.elements = elements; + } + + public String getElements() { + return elements; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Entry.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Entry.java new file mode 100644 index 000000000..0de1741cc --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Entry.java @@ -0,0 +1,179 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.BooleanIntegerSerializer; +import com.gooddata.sdk.common.util.BooleanStringSerializer; +import com.gooddata.sdk.common.util.GDZonedDateTime; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.util.TagsDeserializer; +import com.gooddata.sdk.model.util.TagsSerializer; +import com.gooddata.sdk.model.util.UriHelper; + +import java.time.ZonedDateTime; +import java.util.Set; + +/** + * Metadata entry (can be named "LINK" in some API docs) + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Entry { + + private final String uri; + private final String title; + private final String summary; + private final String category; + private final String author; + private final String contributor; + private final Boolean deprecated; + private final String identifier; + private final Set tags; + @GDZonedDateTime + private final ZonedDateTime created; + @GDZonedDateTime + private final ZonedDateTime updated; + private final Boolean locked; + private final Boolean unlisted; + + @JsonCreator + public Entry(@JsonProperty("link") String uri, + @JsonProperty("title") String title, + @JsonProperty("summary") String summary, + @JsonProperty("category") String category, + @JsonProperty("author") String author, + @JsonProperty("contributor") String contributor, + @JsonProperty("deprecated") @JsonDeserialize(using = BooleanDeserializer.class) Boolean deprecated, + @JsonProperty("identifier") String identifier, + @JsonProperty("tags") @JsonDeserialize(using = TagsDeserializer.class) Set tags, + @JsonProperty("created") ZonedDateTime created, + @JsonProperty("updated") ZonedDateTime updated, + @JsonProperty("locked") @JsonDeserialize(using = BooleanDeserializer.class) Boolean locked, + @JsonProperty("unlisted") @JsonDeserialize(using = BooleanDeserializer.class) Boolean unlisted) { + this.uri = uri; + this.title = title; + this.summary = summary; + this.category = category; + this.author = author; + this.contributor = contributor; + this.deprecated = deprecated; + this.identifier = identifier; + this.tags = tags; + this.created = created; + this.updated = updated; + this.locked = locked; + this.unlisted = unlisted; + } + + /** + * Returns internally generated ID of the object (that's part of the object URI). + * + * @return internal ID of the object + */ + @JsonIgnore + public String getId() { + return UriHelper.getLastUriPart(getUri()); + } + + @JsonProperty("link") + public String getUri() { + return uri; + } + + public String getTitle() { + return title; + } + + public String getSummary() { + return summary; + } + + public String getCategory() { + return category; + } + + public String getAuthor() { + return author; + } + + public String getContributor() { + return contributor; + } + + /** + * @return true if the deprecated is set and is true + */ + @JsonIgnore + public boolean isDeprecated() { + return Boolean.TRUE.equals(deprecated); + } + + @JsonSerialize(using = BooleanStringSerializer.class) + public Boolean getDeprecated() { + return deprecated; + } + + /** + * Returns user-specified identifier of the object. + * + * @return user-specified object identifier + */ + public String getIdentifier() { + return identifier; + } + + @JsonSerialize(using = TagsSerializer.class) + public Set getTags() { + return tags; + } + + public ZonedDateTime getCreated() { + return created; + } + + public ZonedDateTime getUpdated() { + return updated; + } + + /** + * @return true if the locked is set and is true + */ + @JsonIgnore + public boolean isLocked() { + return Boolean.TRUE.equals(locked); + } + + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean getLocked() { + return locked; + } + + /** + * @return true if the unlisted is set and is true + */ + @JsonIgnore + public boolean isUnlisted() { + return Boolean.TRUE.equals(unlisted); + } + + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean getUnlisted() { + return unlisted; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Expression.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Expression.java new file mode 100644 index 000000000..a3676f6b2 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Expression.java @@ -0,0 +1,45 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Expression of fact + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Expression implements Serializable { + + private static final long serialVersionUID = 9161488874222662015L; + private final String data; + private final String type; + + @JsonCreator + public Expression(@JsonProperty("data") String data, @JsonProperty("type") String type) { + this.data = data; + this.type = type; + } + + public String getData() { + return data; + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Fact.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Fact.java new file mode 100644 index 000000000..678ae2e8b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Fact.java @@ -0,0 +1,96 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; + +import static java.util.Collections.singletonList; + +/** + * Fact of GoodData project dataset + */ +@JsonTypeName("fact") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Fact extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = 1394960609414171940L; + + @JsonProperty("content") + private final Content content; + + @JsonCreator + private Fact(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + /* Just for serialization test */ + Fact(String title, String data, String type, String folder) { + super(new Meta(title)); + content = new Content(singletonList(new Expression(data, type)), singletonList(folder)); + } + + @JsonIgnore + public Collection getExpressions() { + return content.getExpression(); + } + + /** + * URIs of folders containing this object + * + * @return collection of URIs or null + */ + @JsonIgnore + public Collection getFolders() { + return content.getFolders(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Content implements Serializable { + + private static final long serialVersionUID = 2141254685536566363L; + + @JsonProperty("expr") + private final Collection expression; + + @JsonProperty("folders") + private final Collection folders; + + @JsonCreator + public Content(@JsonProperty("expr") Collection expression, @JsonProperty("folders") Collection folders) { + this.expression = expression; + this.folders = folders; + } + + public Collection getExpression() { + return expression; + } + + public Collection getFolders() { + return folders; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifierAndUri.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifierAndUri.java new file mode 100644 index 000000000..643dd93c2 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifierAndUri.java @@ -0,0 +1,39 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Encapsulates identifier and its URI. + */ +class IdentifierAndUri { + + private final String identifier; + + private final String uri; + + @JsonCreator + IdentifierAndUri(@JsonProperty("identifier") String identifier, @JsonProperty("uri") String uri) { + this.identifier = identifier; + this.uri = uri; + } + + public String getIdentifier() { + return identifier; + } + + public String getUri() { + return uri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifierToUri.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifierToUri.java new file mode 100644 index 000000000..db0cd64ab --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifierToUri.java @@ -0,0 +1,56 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Structure with list of symbolic names (identifiers) to be expanded to list of URIs. + * Serialization only. + *

+ * See also {@link UriToIdentifier}. + */ +public class IdentifierToUri { + + private final Collection identifiers; + + public IdentifierToUri(final Collection identifiers) { + notNull(identifiers, "identifiers"); + this.identifiers = identifiers; + } + + @JsonProperty("identifierToUri") + public Collection getIdentifiers() { + return identifiers; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IdentifierToUri that = (IdentifierToUri) o; + + if (!identifiers.equals(that.identifiers)) return false; + + return true; + } + + @Override + public int hashCode() { + return identifiers.hashCode(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifiersAndUris.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifiersAndUris.java new file mode 100644 index 000000000..d4fbc6ba7 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/IdentifiersAndUris.java @@ -0,0 +1,75 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Encapsulates list of identifiers and their URIs. + * Deserialization only. + */ +public class IdentifiersAndUris { + + public static final String URI = "/gdc/md/{projectId}/identifiers"; + + private final List identifiersAndUris; + + @JsonCreator + IdentifiersAndUris(@JsonProperty("identifiers") List identifiersAndUris) { + this.identifiersAndUris = identifiersAndUris; + } + + public List getUris() { + return identifiersAndUris.stream().map(IdentifierAndUri::getUri).collect(Collectors.toList()); + } + + @Deprecated + public Map asMap() { + return asIdentifierToUri(); + } + + /** + * Get values as identifier to URI map. + * + * @return map with identifiers as keys, URI as values + */ + public Map asIdentifierToUri() { + if (identifiersAndUris == null) { + return Collections.emptyMap(); + } + + final Map identifierToUri = identifiersAndUris.stream() + .collect(Collectors.toMap(IdentifierAndUri::getIdentifier, IdentifierAndUri::getUri)); + return Collections.unmodifiableMap(identifierToUri); + } + + /** + * Get values as URI to identifier map. + * + * @return map with URI as keys, identifiers as values + */ + public Map asUriToIdentifier() { + if (identifiersAndUris == null) { + return Collections.emptyMap(); + } + + final Map uriToIdentifier = identifiersAndUris.stream() + .collect(Collectors.toMap(IdentifierAndUri::getUri, IdentifierAndUri::getIdentifier)); + return Collections.unmodifiableMap(uriToIdentifier); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/InUseMany.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/InUseMany.java new file mode 100644 index 000000000..6886c24d8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/InUseMany.java @@ -0,0 +1,104 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.BooleanStringSerializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import static com.gooddata.sdk.common.util.Validate.noNullElements; +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.beans.Introspector.decapitalize; + +/** + * UsedBy/Using batch request + */ +@JsonTypeName("inUseMany") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class InUseMany { + + public static final String USEDBY_URI = "/gdc/md/{projectId}/usedby2"; + + private final Collection uris; + + private final Set types; + + private final boolean nearest; + + @JsonCreator + InUseMany(@JsonProperty("uris") Collection uris, + @JsonProperty("nearest") @JsonDeserialize(using = BooleanDeserializer.class) boolean nearest, + @JsonProperty("types") Set types) { + + this.uris = notEmpty(uris, "uris"); + this.types = types; + this.nearest = nearest; + } + + @SafeVarargs + public InUseMany(Collection uris, boolean nearest, Class... type) { + this.uris = notNull(uris, "uris"); + noNullElements(type, "type"); + this.types = new HashSet<>(); + for (Class t : type) { + this.types.add(decapitalize(t.getSimpleName())); + } + this.nearest = nearest; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final InUseMany inUseMany = (InUseMany) o; + + if (nearest != inUseMany.nearest) return false; + if (uris != null ? !uris.equals(inUseMany.uris) : inUseMany.uris != null) return false; + return types != null ? types.equals(inUseMany.types) : inUseMany.types == null; + } + + @Override + public int hashCode() { + int result = uris != null ? uris.hashCode() : 0; + result = 31 * result + (types != null ? types.hashCode() : 0); + result = 31 * result + (nearest ? 1 : 0); + return result; + } + + public Collection getUris() { + return uris; + } + + public Set getTypes() { + return types; + } + + @JsonSerialize(using = BooleanStringSerializer.class) + public boolean isNearest() { + return nearest; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Key.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Key.java new file mode 100644 index 000000000..d9c9b2024 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Key.java @@ -0,0 +1,45 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Key (primary/foreign) of attribute + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Key implements Serializable { + + private static final long serialVersionUID = -2014349746875945306L; + private final String data; + private final String type; + + @JsonCreator + public Key(@JsonProperty("data") String data, @JsonProperty("type") String type) { + this.data = data; + this.type = type; + } + + public String getData() { + return data; + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/MaqlAst.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/MaqlAst.java new file mode 100644 index 000000000..9b9be2384 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/MaqlAst.java @@ -0,0 +1,95 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * MAQL AST representation + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MaqlAst implements Serializable { + + private static final long serialVersionUID = 7392437285242701220L; + private final String type; + private final MaqlAstPosition position; + + @JsonProperty("content") + private MaqlAst[] content; + + /** + * STRING | DATE | INT | FLOAT + */ + @JsonProperty("value") + private String value; + + @JsonCreator + public MaqlAst(@JsonProperty("type") final String type, @JsonProperty("position") final MaqlAstPosition position) { + this.type = type; + this.position = position; + } + + public String getType() { + return type; + } + + public MaqlAstPosition getPosition() { + return position; + } + + public MaqlAst[] getContent() { + return content == null ? null : Arrays.copyOf(content, content.length); + } + + public void setContent(final MaqlAst[] content) { + this.content = content == null ? null : Arrays.copyOf(content, content.length); + } + + public String getValue() { + return value; + } + + public void setValue(final String value) { + this.value = value; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + public static class MaqlAstPosition implements Serializable { + + private final int line; + private final int column; + + @JsonCreator + public MaqlAstPosition(@JsonProperty("column") final int column, @JsonProperty("line") final int line) { + this.column = column; + this.line = line; + } + + public int getColumn() { + return column; + } + + public int getLine() { + return line; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} + diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Meta.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Meta.java new file mode 100644 index 000000000..790efcf58 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Meta.java @@ -0,0 +1,310 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.BooleanIntegerSerializer; +import com.gooddata.sdk.common.util.BooleanStringSerializer; +import com.gooddata.sdk.common.util.GDZonedDateTime; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.util.TagsDeserializer; +import com.gooddata.sdk.model.util.TagsSerializer; +import com.gooddata.sdk.model.util.UriHelper; + +import java.io.Serializable; +import java.time.ZonedDateTime; +import java.util.Set; + +import static org.apache.commons.lang3.StringUtils.substring; + +/** + * Metadata meta information (meant just for internal SDK usage) + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Meta implements Serializable { + + private static final long serialVersionUID = -8102041850487115166L; + private static final int SUMMARY_MAX_LENGTH = 2048; + private static final int TITLE_MAX_LENGTH = 255; + + private String author; + private String contributor; + @GDZonedDateTime + private ZonedDateTime created; + @GDZonedDateTime + private ZonedDateTime updated; + private String summary; + private String title; + private String category; + private Set tags; + private String uri; + private String identifier; + private Boolean deprecated; + private Boolean production; + private Boolean locked; + private Boolean unlisted; + private Boolean sharedWithSomeone; + private Set flags; + + @JsonCreator + protected Meta(@JsonProperty("author") String author, + @JsonProperty("contributor") String contributor, + @JsonProperty("created") ZonedDateTime created, + @JsonProperty("updated") ZonedDateTime updated, + @JsonProperty("summary") String summary, + @JsonProperty("title") String title, + @JsonProperty("category") String category, + @JsonProperty("tags") @JsonDeserialize(using = TagsDeserializer.class) Set tags, + @JsonProperty("uri") String uri, + @JsonProperty("identifier") String identifier, + @JsonProperty("flags") Set flags) { + super(); + this.author = author; + this.uri = uri; + this.tags = tags; + this.created = created; + this.summary = substring(summary, 0, SUMMARY_MAX_LENGTH); + this.title = substring(title, 0, TITLE_MAX_LENGTH); + this.updated = updated; + this.category = category; + this.identifier = identifier; + this.contributor = contributor; + this.flags = flags; + } + + /** + * Constructor with "extra" flags argument + * + * @param author + * @param contributor + * @param created + * @param updated + * @param summary + * @param title + * @param category + * @param tags + * @param uri + * @param identifier + * @param deprecated + * @param production + * @param locked + * @param unlisted + * @param sharedWithSomeone + * @param flags + */ + public Meta(String author, String contributor, ZonedDateTime created, ZonedDateTime updated, String summary, + String title, String category, Set tags, String uri, String identifier, + Boolean deprecated, Boolean production, Boolean locked, Boolean unlisted, + Boolean sharedWithSomeone, Set flags) { + this.author = author; + this.contributor = contributor; + this.created = created; + this.updated = updated; + this.summary = summary; + this.title = title; + this.category = category; + this.tags = tags; + this.uri = uri; + this.identifier = identifier; + this.deprecated = deprecated; + this.production = production; + this.locked = locked; + this.unlisted = unlisted; + this.sharedWithSomeone = sharedWithSomeone; + this.flags = flags; + } + + public Meta(String title) { + this(title, null); + } + + public Meta(String title, String summary) { + this.title = title; + this.summary = summary; + } + + /** + * Returns internally generated ID of the object (that's part of the object URI). + * + * @return internal ID of the object + */ + @JsonIgnore + public String getId() { + return UriHelper.getLastUriPart(getUri()); + } + + public String getAuthor() { + return author; + } + + public String getContributor() { + return contributor; + } + + public ZonedDateTime getCreated() { + return created; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public ZonedDateTime getUpdated() { + return updated; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + @JsonSerialize(using = TagsSerializer.class) + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + this.tags = tags; + } + + public String getUri() { + return uri; + } + + /** + * Default is false/not-deprecated. + * + * @return true when the linked object is deprecated, null if not set + */ + @JsonProperty("deprecated") + @JsonSerialize(using = BooleanStringSerializer.class) + public Boolean isDeprecated() { + return deprecated; + } + + @JsonProperty("deprecated") + @JsonDeserialize(using = BooleanDeserializer.class) + public void setDeprecated(Boolean deprecated) { + this.deprecated = deprecated; + } + + /** + * Returns user-specified identifier of the object. + * + * @return user-specified object identifier + */ + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Is the object production or not? Defaults to true. + * + * @return true when the linked object is production, null if not set + */ + @JsonProperty("isProduction") + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean isProduction() { + return production; + } + + @JsonProperty("isProduction") + @JsonDeserialize(using = BooleanDeserializer.class) + public void setProduction(final Boolean production) { + this.production = production; + } + + /** + * Flag that MD object is locked; default is false/unlocked. + * + * @return true when the linked object is locked, null if not set + */ + @JsonProperty("locked") + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean isLocked() { + return locked; + } + + @JsonProperty("locked") + @JsonDeserialize(using = BooleanDeserializer.class) + public void setLocked(Boolean locked) { + this.locked = locked; + } + + /** + * Default is false/listed. + * + * @return true when the linked object is unlisted, null if not set + */ + @JsonProperty("unlisted") + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean isUnlisted() { + return unlisted; + } + + @JsonProperty("unlisted") + @JsonDeserialize(using = BooleanDeserializer.class) + public void setUnlisted(Boolean unlisted) { + this.unlisted = unlisted; + } + + /** + * Is the linked object shared with someone via ACLs? + * + * @return true when the linked object is shared, null if not set + */ + @JsonProperty("sharedWithSomeone") + @JsonSerialize(using = BooleanIntegerSerializer.class) + public Boolean isSharedWithSomeone() { + return sharedWithSomeone; + } + + @JsonProperty("sharedWithSomeone") + @JsonDeserialize(using = BooleanDeserializer.class) + public void setSharedWithSomeone(final Boolean sharedWithSomeone) { + this.sharedWithSomeone = sharedWithSomeone; + } + + public Set getFlags() { + return flags; + } + + public void setFlags(final Set flags) { + this.flags = flags; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Metric.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Metric.java new file mode 100644 index 000000000..ccea81426 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Metric.java @@ -0,0 +1,126 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; + +/** + * Metric + */ +@JsonTypeName("metric") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties("links") +public class Metric extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = -1666713447809179661L; + + @JsonProperty("content") + private final Content content; + + @JsonCreator + private Metric(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + public Metric(String title, String expression, String format) { + this(new Meta(title), new Metric.Content(expression, format)); + } + + @JsonIgnore + public String getExpression() { + return content.getExpression(); + } + + @JsonIgnore + public String getFormat() { + return content.getFormat(); + } + + @JsonIgnore + public MaqlAst getMaqlAst() { + return content.getMaqlAst(); + } + + /** + * URIs of folders containing this object + * + * @return collection of URIs or null + */ + @JsonIgnore + public Collection getFolders() { + return content.getFolders(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Content implements Serializable { + + private static final long serialVersionUID = 7959588028233637749L; + private final String expression; + private final Collection folders; + @JsonProperty("format") + private String format; + @JsonProperty("tree") + private MaqlAst maqlAst; + + @JsonCreator + public Content(@JsonProperty("expression") String expression, @JsonProperty("folders") Collection folders) { + this.expression = expression; + this.folders = folders; + } + + public Content(final String expression, final String format) { + this.expression = expression; + this.format = format; + this.folders = null; + } + + public String getExpression() { + return expression; + } + + public String getFormat() { + return format; + } + + public void setFormat(final String format) { + this.format = format; + } + + public MaqlAst getMaqlAst() { + return maqlAst; + } + + public void setMaqlAst(final MaqlAst maqlAst) { + this.maqlAst = maqlAst; + } + + public Collection getFolders() { + return folders; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/NestedAttribute.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/NestedAttribute.java new file mode 100644 index 000000000..487844dcb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/NestedAttribute.java @@ -0,0 +1,272 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; + +/** + * Attribute representation which is nested in some other metadata object (i.e. within {@link Dimension}). + * Can't be queried, get or updated directly - use {@link Attribute} for these operations. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class NestedAttribute extends AbstractObj { + + private static final long serialVersionUID = -5733300152707435624L; + + @JsonProperty("content") + private final Content content; + + @JsonCreator + protected NestedAttribute(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + @JsonIgnore + public Collection getDisplayForms() { + return content.getDisplayForms(); + } + + @JsonIgnore + public Collection getPrimaryKeys() { + return content.getPk(); + } + + @JsonIgnore + public Collection getForeignKeys() { + return content.getFk(); + } + + @JsonIgnore + public DisplayForm getDefaultDisplayForm() { + return getDisplayForms().iterator().next(); + } + + @JsonIgnore + public String getDimensionUri() { + return content.getDimensionUri(); + } + + public boolean hasDimension() { + return getDimensionUri() != null; + } + + @JsonIgnore + public Collection getRelations() { + return content.getRelations(); + } + + @JsonIgnore + public String getDirection() { + return content.getDirection(); + } + + /** + * @return sort setting - pk, byUsedDF or uri linking some display form, null if not set + * @see #isSortedByLinkedDf() + * @see #isSortedByUsedDf() + * @see #isSortedByPk() + */ + @JsonIgnore + public String getSort() { + return content.getSort() != null ? content.getSort().getValue() : null; + } + + /** + * @return true when the sort is set and it is a link to display form, false otherwise + */ + @JsonIgnore + public boolean isSortedByLinkedDf() { + return content.getSort() != null && content.getSort().isLinkType(); + } + + /** + * @return true when the sort is set to byUsedDF (used display form), false otherwise + */ + @JsonIgnore + public boolean isSortedByUsedDf() { + return content.getSort() != null && AttributeSort.BY_USED_DF.equals(content.getSort().getValue()); + } + + /** + * @return true when the sort is set to pk (primary key), false otherwise + */ + @JsonIgnore + public boolean isSortedByPk() { + return content.getSort() != null && AttributeSort.PK.equals(content.getSort().getValue()); + } + + @JsonIgnore + public String getType() { + return content.getType(); + } + + @JsonIgnore + public Collection getCompositeAttribute() { + return content.getCompositeAttribute(); + } + + @JsonIgnore + public Collection getCompositeAttributePk() { + return content.getCompositeAttributePk(); + } + + @JsonIgnore + public String getDrillDownStepDisplayFormUri() { + return content.getDrillDownStepDisplayFormUri(); + } + + @JsonIgnore + public String getLinkedDisplayFormUri() { + return content.getLinkedDisplayFormUri(); + } + + /** + * URIs of folders containing this object + * + * @return collection of URIs or null + */ + @JsonIgnore + public Collection getFolders() { + return content.getFolders(); + } + + @JsonIgnore + public Collection getGrain() { + return content.getGrain(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + protected static class Content implements Serializable { + + private static final long serialVersionUID = -6828025725075482356L; + private final Collection pk; + private final Collection fk; + private final Collection displayForms; + private final String dimension; + private final String direction; + private final AttributeSort sort; + private final String type; + private final Collection rel; + private final Collection compositeAttribute; + private final Collection compositeAttributePk; + private final String drillDownStepAttributeDF; + private final String linkedDisplayFormUri; + private final Collection folders; + private final Collection grain; + + @JsonCreator + protected Content(@JsonProperty("pk") Collection pk, + @JsonProperty("fk") Collection fk, + @JsonProperty("displayForms") Collection displayForms, + @JsonProperty("dimension") String dimension, + @JsonProperty("direction") String direction, + @JsonProperty("sort") AttributeSort sort, + @JsonProperty("type") String type, + @JsonProperty("rel") Collection rel, + @JsonProperty("compositeAttribute") Collection compositeAttribute, + @JsonProperty("compositeAttributePk") Collection compositeAttributePk, + @JsonProperty("drillDownStepAttributeDF") String drillDownStepAttributeDF, + @JsonProperty("linkAttributeDF") String linkedDisplayFormUri, + @JsonProperty("folders") Collection folders, + @JsonProperty("grain") Collection grain) { + this.pk = pk; + this.fk = fk; + this.displayForms = displayForms; + this.dimension = dimension; + this.direction = direction; + this.sort = sort; + this.type = type; + this.rel = rel; + this.compositeAttribute = compositeAttribute; + this.compositeAttributePk = compositeAttributePk; + this.drillDownStepAttributeDF = drillDownStepAttributeDF; + this.linkedDisplayFormUri = linkedDisplayFormUri; + this.folders = folders; + this.grain = grain; + } + + public Collection getPk() { + return pk; + } + + public Collection getFk() { + return fk; + } + + public Collection getDisplayForms() { + return displayForms; + } + + @JsonProperty("dimension") + public String getDimensionUri() { + return dimension; + } + + public String getDirection() { + return direction; + } + + public AttributeSort getSort() { + return sort; + } + + public String getType() { + return type; + } + + @JsonProperty("rel") + public Collection getRelations() { + return rel; + } + + public Collection getCompositeAttribute() { + return compositeAttribute; + } + + public Collection getCompositeAttributePk() { + return compositeAttributePk; + } + + @JsonProperty("drillDownStepAttributeDF") + public String getDrillDownStepDisplayFormUri() { + return drillDownStepAttributeDF; + } + + @JsonProperty("linkAttributeDF") + public String getLinkedDisplayFormUri() { + return linkedDisplayFormUri; + } + + public Collection getFolders() { + return folders; + } + + public Collection getGrain() { + return grain; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Obj.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Obj.java new file mode 100644 index 000000000..90bf5e414 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Obj.java @@ -0,0 +1,45 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.gooddata.sdk.model.md.report.Report; +import com.gooddata.sdk.model.md.report.ReportDefinition; +import com.gooddata.sdk.model.md.visualization.VisualizationClass; +import com.gooddata.sdk.model.md.visualization.VisualizationObject; + +/** + * First class metadata object - only dto objects, which have URI pointing to themselves should implement this. + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) +@JsonSubTypes({ + @JsonSubTypes.Type(Attribute.class), + @JsonSubTypes.Type(AttributeDisplayForm.class), + @JsonSubTypes.Type(Column.class), + @JsonSubTypes.Type(DataLoadingColumn.class), + @JsonSubTypes.Type(Dataset.class), + @JsonSubTypes.Type(Dimension.class), + @JsonSubTypes.Type(Fact.class), + @JsonSubTypes.Type(Metric.class), + @JsonSubTypes.Type(ProjectDashboard.class), + @JsonSubTypes.Type(Report.class), + @JsonSubTypes.Type(ReportDefinition.class), + @JsonSubTypes.Type(ScheduledMail.class), + @JsonSubTypes.Type(Table.class), + @JsonSubTypes.Type(TableDataLoad.class), + @JsonSubTypes.Type(VisualizationObject.class), + @JsonSubTypes.Type(VisualizationClass.class), +}) +public interface Obj { + + String URI = "/gdc/md/{projectId}/obj"; + String CREATE_URI = URI + "?createAndGet=true"; + String CREATE_WITH_ID_URI = CREATE_URI + "&setIdentifier=true"; + String OBJ_URI = URI + "/{objId}"; + + String getUri(); +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ProjectDashboard.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ProjectDashboard.java new file mode 100644 index 000000000..6db262a7e --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ProjectDashboard.java @@ -0,0 +1,141 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.util.Arrays.asList; + +/** + * Project Dashboard of GoodData project.
+ * Deserialization only. This object is not complete representation of real 'projectDashboard' object. + * It contains only dashboard tabs basic definitions (identifier and name). + */ +@JsonTypeName("projectDashboard") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ProjectDashboard extends AbstractObj implements Queryable { + + private static final long serialVersionUID = 6791559547484536326L; + private final Content content; + + private ProjectDashboard(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = notNull(content, "content"); + } + + public ProjectDashboard(final String title, final Tab... tabs) { + this(new Meta(title), new Content(asList(tabs))); + } + + @JsonIgnore + public Collection getTabs() { + return content.tabs; + } + + /** + * Returns dashboard tab with the given tab name. + * If tab with such name doesn't exist, returns {@code null}. + * + * @param name tab name + * @return

    + *
  • dashboard tab with the given name
  • + *
  • {@code null} if tab doesn't exist
  • + *
+ */ + @JsonIgnore + public Tab getTabByName(String name) { + for (Tab tab : getTabs()) { + if (Objects.equals(tab.title, name)) { + return tab; + } + } + return null; + } + + public Content getContent() { + return content; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Content implements Serializable { + + private static final long serialVersionUID = -3540410868419025134L; + private final Collection tabs; + + @JsonProperty("filters") + private final Collection filters = Collections.emptyList(); // dummy just to make input validation pass + + @JsonCreator + private Content(@JsonProperty("tabs") Collection tabs) { + this.tabs = tabs; + } + + public Collection getTabs() { + return tabs; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class Tab implements Serializable { + + private static final long serialVersionUID = 4755791353382871571L; + private final String identifier; + private final String title; + + @JsonProperty("items") + private final Collection items = Collections.emptyList(); // dummy just to make input validation pass + + @JsonCreator + private Tab(@JsonProperty("identifier") String identifier, @JsonProperty("title") String title) { + this.identifier = identifier; + this.title = title; + } + + public Tab(final String title) { + this(null, title); + } + + public String getIdentifier() { + return identifier; + } + + public String getTitle() { + return title; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Query.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Query.java new file mode 100644 index 000000000..306757d3c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Query.java @@ -0,0 +1,93 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +/** + * Metadata query result + */ +@JsonTypeName("query") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Query { + + public static final String URI = "/gdc/md/{projectId}/query/{type}"; + + private final Collection entries; + + private final Meta meta; + + @JsonCreator + private Query(@JsonProperty("entries") Collection entries, @JsonProperty("meta") Meta meta) { + this.entries = entries; + this.meta = meta; + } + + public Collection getEntries() { + return entries; + } + + public String getCategory() { + return meta.getCategory(); + } + + public String getSummary() { + return meta.getSummary(); + } + + public String getTitle() { + return meta.getTitle(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Meta { + private final String category; + private final String summary; + private final String title; + + @JsonCreator + public Meta(@JsonProperty("category") String category, @JsonProperty("summary") String summary, + @JsonProperty("title") String title) { + this.category = category; + this.summary = summary; + this.title = title; + } + + public String getCategory() { + return category; + } + + public String getSummary() { + return summary; + } + + public String getTitle() { + return title; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Queryable.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Queryable.java new file mode 100644 index 000000000..aeb88f367 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Queryable.java @@ -0,0 +1,12 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +/** + * Marker interface for metadata objects which can be found using query resource (see MetadataService.find* methods). + */ +public interface Queryable extends Obj { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ReportAttachment.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ReportAttachment.java new file mode 100644 index 000000000..acca59898 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ReportAttachment.java @@ -0,0 +1,135 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.export.ExportFormat; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +/** + * Attachment to {@link ScheduledMail} represents report-related information for the schedule. + */ +public class ReportAttachment extends Attachment { + + private final Collection formats; + private final Map exportOptions; + + @JsonCreator + protected ReportAttachment( + @JsonProperty("uri") String uri, + @JsonProperty("exportOptions") Map exportOptions, + @JsonProperty("formats") String... formats + ) { + super(uri); + this.exportOptions = exportOptions; + this.formats = Arrays.asList(formats); + } + + protected ReportAttachment(String uri, Map exportOptions, ExportFormat... formats) { + this(uri, exportOptions, ExportFormat.arrayToStringArray(formats)); + } + + /** + * Options which modify default export behavior. Due to variety of + * export formats options only work for explicitly listed + * format types. + * + *
    + *
  • pageOrientation + *
      + *
    • set page orientation
    • + *
    • default value: 'portrait'
    • + *
    • supported in: tabular PDF
    • + *
    + *
  • + * + *
  • optimalColumnWidth + *
      + *
    • set 'yes' to automatically resize all columns to fit cell's content
    • + *
    • default is: 'no' (do not auto resize)
    • + *
    • supported in: tabular PDF
    • + *
    + *
  • + * + *
  • pageScalePercentage + *
      + *
    • down-scaling factor (in percent), 100 means no scale-down
    • + *
    • may not be combined with scaleToPages, scaleToPagesX and scaleToPagesY
    • + *
    • default is: 100
    • + *
    • supported in: tabular PDF
    • + *
    + *
  • + * + *
  • scaleToPages + *
      + *
    • total number of pages of target PDF file
    • + *
    • may not be combined with pageScalePercentage, scaleToPagesX and scaleToPagesY
    • + *
    • default is: unlimited
    • + *
    • supported in: tabular PDF
    • + *
    + *
  • + * + *
  • scaleToPagesX + *
      + *
    • number of horizontal pages of target PDF file
    • + *
    • may not be combined with pageScalePercentage and scaleToPages, but may be combined with scaleToPagesY
    • + *
    • default is: unlimited
    • + *
    • supported in: tabular PDF
    • + *
    + *
  • + * + *
  • scaleToPagesY + *
      + *
    • number of vertical pages of target PDF file
    • + *
    • may not be combined with pageScalePercentage and scaleToPages, but may be combined with scaleToPagesX
    • + *
    • default is: unlimited
    • + *
    • supported in: tabular PDF
    • + *
    + *
  • + *
+ * + * @return map of export options + */ + public Map getExportOptions() { + return exportOptions; + } + + public Collection getFormats() { + return formats; + } + + @Override + public boolean equals(Object o) { + if (!super.equals(o)) return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ReportAttachment that = (ReportAttachment) o; + + if (formats != null ? !formats.equals(that.formats) : that.formats != null) return false; + if (getUri() != null ? !getUri().equals(that.getUri()) : that.getUri() != null) return false; + return !(exportOptions != null ? !exportOptions.equals(that.exportOptions) : that.exportOptions != null); + + } + + @Override + public int hashCode() { + int result = formats != null ? formats.hashCode() : 0; + result = 31 * result + (exportOptions != null ? exportOptions.hashCode() : 0); + result = 31 * result + (getUri() != null ? getUri().hashCode() : 0); + return result; + } + + @Override + public String toString() { + return new GoodDataToStringBuilder(this).append("uri", getUri()).toString(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Restriction.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Restriction.java new file mode 100644 index 000000000..9d6aa3972 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Restriction.java @@ -0,0 +1,88 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Objects; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Metadata query restriction. See static factory methods to get instance of desired restriction type. + */ +public class Restriction { + + private final Type type; + + private final String value; + + private Restriction(Type type, String value) { + this.type = notNull(type, "type"); + this.value = notNull(value, "value"); + } + + /** + * Construct a new instance with restriction type identifier and given value. + * + * @param value identifier you want to search for + * @return new restriction for identifier restriction + */ + public static Restriction identifier(String value) { + return new Restriction(Type.IDENTIFIER, value); + } + + /** + * Construct a new instance with restriction type title and given value. + * + * @param value title you want to search for + * @return new restriction for title restriction + */ + public static Restriction title(String value) { + return new Restriction(Type.TITLE, value); + } + + /** + * Construct a new instance with restriction type summary and given value. + * + * @param value summary you want to search for + * @return new restriction for summary restriction + */ + public static Restriction summary(String value) { + return new Restriction(Type.SUMMARY, value); + } + + public Type getType() { + return type; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final Restriction that = (Restriction) o; + return type == that.type && + value.equals(that.value); + } + + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + public enum Type { + IDENTIFIER, TITLE, SUMMARY + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ScheduledMail.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ScheduledMail.java new file mode 100644 index 000000000..9e03136e0 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ScheduledMail.java @@ -0,0 +1,303 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.export.ExportFormat; +import com.gooddata.sdk.model.md.report.ReportDefinition; + +import java.io.Serializable; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * A scheduled mail MD object. It represents a schedule on mail-sending of + * exported dashboards and reports. + */ +@JsonTypeName("scheduledMail") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ScheduledMail extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = -4433891105896592638L; + + @JsonProperty("content") + private final Content content; + + @JsonCreator + ScheduledMail(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + private ScheduledMail(String title, String summary, Set tags, boolean deprecated, String recurrency, + LocalDate startDate, String timeZone, Collection toAddresses, + Collection bccAddresses, String subject, String body, List attachments) { + super(new Meta(null, null, null, null, summary, title, null, tags, null, null, deprecated, null, false, false, + null, null)); + notNull(toAddresses, "toAddresses"); + notNull(subject, "subject"); + notNull(body, "body"); + notNull(attachments, "attachments"); + content = new Content(new ScheduledMailWhen(recurrency, startDate, timeZone), toAddresses, bccAddresses, + subject, body, attachments); + } + + /** + * Creates an almost empty instance of the object. It's up to the user's responsibility to call all the necessary setters. + * + * @param title the title of the MD object + * @param summary the summary of the MD object + */ + public ScheduledMail(String title, String summary) { + super(new Meta(null, null, null, null, summary, title, null, Collections.emptySet(), null, null, false, + null, false, false, null, null)); + this.content = new Content(); + } + + /** + * Creates full, safe mail schedule object. + * + * @param title the title of the MD object + * @param summary the summary of the MD object + * @param recurrency schedule in format defined in schedule + * @param startDate schedule starting date + * @param timeZone time zone of the starting date + * @param toAddresses collection of email addresses to send the mail to + * @param bccAddresses collection of blind copy addresses to send the mail to + * @param subject the subject of the scheduled mail + * @param body the text body of the scheduled mail + * @param attachments reports and dashboards to send in the scheduled email + */ + public ScheduledMail(String title, String summary, String recurrency, LocalDate startDate, String timeZone, + Collection toAddresses, Collection bccAddresses, String subject, String body, + List attachments) { + this(title, summary, Collections.emptySet(), false, recurrency, startDate, timeZone, toAddresses, + bccAddresses, subject, body, attachments); + } + + @JsonIgnore + public ScheduledMailWhen getWhen() { + return content.getScheduledMailWhen(); + } + + @JsonIgnore + public Collection getToAddresses() { + return content.getToAddresses(); + } + + @JsonIgnore + public Collection getBccAddresses() { + return content.getBccAddresses(); + } + + @JsonIgnore + public String getSubject() { + return content.getSubject(); + } + + public ScheduledMail setSubject(String subject) { + this.content.setSubject(subject); + return this; + } + + @JsonIgnore + public String getBody() { + return content.getBody(); + } + + public ScheduledMail setBody(String body) { + this.content.setBody(body); + return this; + } + + @JsonIgnore + public Collection getAttachments() { + return content.getAttachments(); + } + + public ScheduledMail setAttachments(List attachments) { + this.content.setAttachments(attachments); + return this; + } + + public ScheduledMail setRecurrency(String recurrency) { + this.content.getScheduledMailWhen().setRecurrency(recurrency); + return this; + } + + public ScheduledMail setStartDate(LocalDate startDate) { + this.content.getScheduledMailWhen().setStartDate(startDate); + return this; + } + + public ScheduledMail setTimeZone(String timeZone) { + this.content.getScheduledMailWhen().setTimeZone(timeZone); + return this; + } + + public ScheduledMail setTo(Collection toAddresses) { + this.content.setToAddress(toAddresses); + return this; + } + + public ScheduledMail setBcc(Collection bccAddresses) { + this.content.setBccAddress(bccAddresses); + return this; + } + + public ScheduledMail addToAddress(String toAdd) { + this.content.getToAddresses().add(toAdd); + return this; + } + + public ScheduledMail addBccAddress(String bccAdd) { + this.content.getBccAddresses().add(bccAdd); + return this; + } + + public ScheduledMail addReportAttachment(ReportDefinition reportDefinition, Map exportOptions, String... formats) { + notNull(formats, "formats"); + ReportAttachment ra = new ReportAttachment(reportDefinition.getUri(), exportOptions, formats); + this.content.getAttachments().add(ra); + return this; + } + + public ScheduledMail addReportAttachment(ReportDefinition reportDefinition, Map exportOptions, ExportFormat... formats) { + return addReportAttachment(reportDefinition, exportOptions, ExportFormat.arrayToStringArray(formats)); + } + + public ScheduledMail addDashboardAttachment(String uri, Integer allTabs, String executionContext, String... tabs) { + notNull(tabs, "tabs"); + DashboardAttachment da = new DashboardAttachment(uri, allTabs, executionContext, tabs); + this.content.getAttachments().add(da); + return this; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + /** + * Mail schedule MD object payload. + */ + private static class Content implements Serializable { + + private static final long serialVersionUID = -6676890634279968527L; + + @JsonProperty("when") + private ScheduledMailWhen scheduledMailWhen; + + /** + * Collection of email addresses. + */ + @JsonProperty("to") + private Collection toAddress; + + /** + * Collection of email addresses. + */ + @JsonProperty("bcc") + private Collection bccAddress; + private String subject; + private String body; + private Collection attachments; + + @JsonCreator + public Content( + @JsonProperty("when") ScheduledMailWhen scheduledMailWhen, + @JsonProperty("to") Collection toAddress, + @JsonProperty("bcc") Collection bccAddress, + @JsonProperty("subject") String subject, + @JsonProperty("body") String body, + @JsonProperty("attachments") Collection attachments) { + + this.scheduledMailWhen = scheduledMailWhen; + this.toAddress = toAddress; + this.bccAddress = bccAddress; + this.subject = subject; + this.body = body; + this.attachments = attachments; + } + + public Content() { + this.scheduledMailWhen = new ScheduledMailWhen(); + this.toAddress = new ArrayList<>(); + this.bccAddress = new ArrayList<>(); + this.attachments = new ArrayList<>(); + } + + @JsonIgnore + public ScheduledMailWhen getScheduledMailWhen() { + return scheduledMailWhen; + } + + public void setScheduledMailWhen(ScheduledMailWhen scheduledMailWhen) { + this.scheduledMailWhen = scheduledMailWhen; + } + + @JsonIgnore + public Collection getToAddresses() { + return toAddress; + } + + @JsonIgnore + public Collection getBccAddresses() { + return bccAddress; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Collection getAttachments() { + return attachments; + } + + public void setAttachments(Collection attachments) { + this.attachments = attachments; + } + + public void setToAddress(Collection toAddress) { + this.toAddress = toAddress; + } + + public void setBccAddress(Collection bccAddress) { + this.bccAddress = bccAddress; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ScheduledMailWhen.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ScheduledMailWhen.java new file mode 100644 index 000000000..106c5e93b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/ScheduledMailWhen.java @@ -0,0 +1,97 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GDLocalDate; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.time.LocalDate; + +/** + * Represents the start date and cron-like expression for {@link ScheduledMail} mail schedule. + */ +public class ScheduledMailWhen implements Serializable { + + private static final long serialVersionUID = 1203170008606357967L; + + /** + * Cron like recurrency pattern. Example: "0:0:0:1*12:0:0". + * For details, please see this comprehensive documentation. + */ + private String recurrency; + @GDLocalDate + @JsonInclude(JsonInclude.Include.NON_NULL) + private LocalDate startDate; + private String timeZone; + + @JsonCreator + protected ScheduledMailWhen(@JsonProperty("recurrency") String recurrency, + @JsonProperty("startDate") LocalDate startDate, + @JsonProperty("timeZone") String timeZone) { + this.recurrency = recurrency; + this.startDate = startDate; + this.timeZone = timeZone; + } + + public ScheduledMailWhen() { + } + + public String getRecurrency() { + return recurrency; + } + + public void setRecurrency(String recurrency) { + this.recurrency = recurrency; + } + + public LocalDate getStartDate() { + return startDate; + } + + public void setStartDate(LocalDate startDate) { + this.startDate = startDate; + } + + public String getTimeZone() { + return timeZone; + } + + public void setTimeZone(String timeZone) { + this.timeZone = timeZone; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ScheduledMailWhen scheduledMailWhen = (ScheduledMailWhen) o; + + if (recurrency != null ? !recurrency.equals(scheduledMailWhen.recurrency) : scheduledMailWhen.recurrency != null) + return false; + if (startDate != null ? !startDate.equals(scheduledMailWhen.startDate) : scheduledMailWhen.startDate != null) + return false; + return !(timeZone != null ? !timeZone.equals(scheduledMailWhen.timeZone) : scheduledMailWhen.timeZone != null); + + } + + @Override + public int hashCode() { + int result = recurrency != null ? recurrency.hashCode() : 0; + result = 31 * result + (startDate != null ? startDate.hashCode() : 0); + result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Service.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Service.java new file mode 100644 index 000000000..e5db880b5 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Service.java @@ -0,0 +1,49 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Represents project/workspace metadata configuration. + */ +@JsonTypeName("service") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Service implements Serializable { + + public static final String TIMEZONE_URI = "/gdc/md/{projectId}/service/timezone"; + + private static final long serialVersionUID = -3382672258337809805L; + + private final String timezone; + + @JsonCreator + public Service(@JsonProperty("timezone") String timezone) { + this.timezone = timezone; + } + + /** + * @return string identifier of the timezone (see IANA/Olson tz database for possible values) + */ + public String getTimezone() { + return timezone; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Table.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Table.java new file mode 100644 index 000000000..514353859 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Table.java @@ -0,0 +1,107 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; + +/** + * Represents physical data model table. + * Deserialization only. + */ +@JsonTypeName("table") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public class Table extends AbstractObj implements Queryable { + + private static final long serialVersionUID = 8188850708608710066L; + private final Content content; + + @JsonCreator + private Table(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + /** + * @return name of the table in DB + */ + public String getDBName() { + return content.getTableDBName(); + } + + /** + * @return collection of {@link TableDataLoad}'s uris, can return null + */ + public Collection getDataLoads() { + return content.getTableDataLoads(); + } + + /** + * @return table weight, can return null + */ + public Integer getWeight() { + return content.getWeight(); + } + + /** + * @return uri of active {@link TableDataLoad}, can return null + */ + public String getActiveDataLoad() { + return content.getActiveDataLoad(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Content implements Serializable { + + private static final long serialVersionUID = -3487498890189149368L; + private final String tableDBName; + private final String activeDataLoad; + private final Collection tableDataLoads; + private final Integer weight; + + @JsonCreator + private Content(@JsonProperty("tableDBName") String tableDBName, @JsonProperty("activeDataLoad") String activeDataLoad, + @JsonProperty("tableDataLoad") Collection tableDataLoads, @JsonProperty("weight") Integer weight) { + this.tableDBName = tableDBName; + this.activeDataLoad = activeDataLoad; + this.tableDataLoads = tableDataLoads; + this.weight = weight; + } + + public String getTableDBName() { + return tableDBName; + } + + public String getActiveDataLoad() { + return activeDataLoad; + } + + public Collection getTableDataLoads() { + return tableDataLoads; + } + + public Integer getWeight() { + return weight; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/TableDataLoad.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/TableDataLoad.java new file mode 100644 index 000000000..f5b9c22c5 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/TableDataLoad.java @@ -0,0 +1,95 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +/** + * Represents data load of physical table. + * Deserialization only. + */ +@JsonTypeName("tableDataLoad") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public class TableDataLoad extends AbstractObj implements Queryable { + + public static final String TYPE_FULL = "full"; + public static final String TYPE_INCREMENTAL = "incremental"; + private static final long serialVersionUID = -5209417147612785042L; + private final Content content; + + @JsonCreator + private TableDataLoad(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + /** + * @return location of load data source + */ + public String getDataSourceLocation() { + return content.getDataSourceLocation(); + } + + /** + * @return true if the type is full, false otherwise + */ + public boolean isFull() { + return TYPE_FULL.equals(getType()); + } + + /** + * @return true if the type is incremental, false otherwise + */ + public boolean isIncremental() { + return TYPE_INCREMENTAL.equals(getType()); + } + + /** + * @return type of the load, one of full,incremental + */ + public String getType() { + return content.getTypeOfLoad(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Content implements Serializable { + + private static final long serialVersionUID = 3566067131546271394L; + private final String dataSourceLocation; + private final String typeOfLoad; + + @JsonCreator + private Content(@JsonProperty("dataSourceLocation") String dataSourceLocation, @JsonProperty("typeOfLoad") String typeOfLoad) { + this.dataSourceLocation = dataSourceLocation; + this.typeOfLoad = typeOfLoad; + } + + public String getDataSourceLocation() { + return dataSourceLocation; + } + + public String getTypeOfLoad() { + return typeOfLoad; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Updatable.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Updatable.java new file mode 100644 index 000000000..13aa0f5c3 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Updatable.java @@ -0,0 +1,12 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +/** + * Marker interface for metadata objects which can be updated. (see {@link MetadataService#updateObj(Updatable)}). + */ +public interface Updatable extends Obj { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UriToIdentifier.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UriToIdentifier.java new file mode 100644 index 000000000..f97a97de7 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UriToIdentifier.java @@ -0,0 +1,56 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Structure with list of URIs to be expanded to list of symbolic names (identifiers). + * Serialization only. + *

+ * See also {@link IdentifierToUri}. + */ +public class UriToIdentifier { + + private final Collection uris; + + public UriToIdentifier(final Collection uris) { + notNull(uris, "uris"); + this.uris = uris; + } + + @JsonProperty("uriToIdentifier") + public Collection getUris() { + return uris; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UriToIdentifier that = (UriToIdentifier) o; + + if (!uris.equals(that.uris)) return false; + + return true; + } + + @Override + public int hashCode() { + return uris.hashCode(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Usage.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Usage.java new file mode 100644 index 000000000..1a68c9746 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/Usage.java @@ -0,0 +1,44 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +/** + * Describes object usages. Object is represented by its URI and objects using it by collection of entries. + */ +public class Usage { + + private final String uri; + + private final Collection usedBy; + + /** + * Constructs object. + * + * @param uri object URI + * @param usedBy using objects + */ + public Usage(final String uri, final Collection usedBy) { + this.uri = uri; + this.usedBy = usedBy; + } + + public String getUri() { + return uri; + } + + public Collection getUsedBy() { + return usedBy; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UseMany.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UseMany.java new file mode 100644 index 000000000..5dc8b3175 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UseMany.java @@ -0,0 +1,40 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * UsedBy/Using batch result + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UseMany { + + private final Collection useMany; + + @JsonCreator + private UseMany(@JsonProperty("useMany") final Collection useMany) { + this.useMany = notNull(useMany, "useMany"); + } + + public Collection getUseMany() { + return useMany; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UseManyEntries.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UseManyEntries.java new file mode 100644 index 000000000..0ae541db6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/UseManyEntries.java @@ -0,0 +1,43 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UseManyEntries { + + private final String uri; + + private final Collection entries; + + @JsonCreator + UseManyEntries(@JsonProperty("uri") final String uri, + @JsonProperty("entries") final Collection entries) { + this.uri = uri; + this.entries = entries; + } + + public String getUri() { + return uri; + } + + public Collection getEntries() { + return entries; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/AnalyticalDashboard.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/AnalyticalDashboard.java new file mode 100644 index 000000000..4ef5337f9 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/AnalyticalDashboard.java @@ -0,0 +1,102 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Represents analytical dashboard configuration. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("analyticalDashboard") +@JsonIgnoreProperties(ignoreUnknown = true) +public class AnalyticalDashboard extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = -2217112261771452747L; + + private final Content content; + + /** + * Constructor. + * + * @param title dashboard title + * @param widgetUris URIs of widgets located on dashboard + * @param filtersContextUri URI of filters context applied to this dashboard (optional) + */ + public AnalyticalDashboard(final String title, final List widgetUris, final String filtersContextUri) { + this(new Meta(title), new Content(notNull(widgetUris, "widgetUris"), filtersContextUri)); + } + + @JsonCreator + private AnalyticalDashboard(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + @JsonProperty + private Content getContent() { + return content; + } + + /** + * @return URIs of widgets contained in this dashboard + */ + @JsonIgnore + public List getWidgetUris() { + return Collections.unmodifiableList(getContent().getWidgets()); + } + + /** + * @return URI of filters context applied to this dashboard + */ + @JsonIgnore + public String getFiltersContextUri() { + return getContent().getFilterContext(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Content implements Serializable { + + private static final long serialVersionUID = 3492968666803382202L; + + private final List widgets; + private final String filterContext; + + @JsonCreator + private Content( + @JsonProperty("widgets") final List widgets, + @JsonProperty("filterContext") final String filterContext) { + this.widgets = widgets; + this.filterContext = filterContext; + } + + @JsonProperty + private List getWidgets() { + return widgets; + } + + @JsonProperty + private String getFilterContext() { + return filterContext; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/Kpi.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/Kpi.java new file mode 100644 index 000000000..ff8427969 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/Kpi.java @@ -0,0 +1,180 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; +import com.gooddata.sdk.model.md.dashboard.filter.FilterReference; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Represents KPI (key performance indicator) for analytical dashboard. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("kpi") +@JsonIgnoreProperties(ignoreUnknown = true) +public class Kpi extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = -7747260758488157192L; + + private static final String NONE_COMPARISON_TYPE = "none"; + + private final Content content; + + /** + * Creates new KPI for a given metric with some date filter and comparison + * + * @param title title of KPI + * @param metricUri URI of the KPI metric + * @param comparisonType KPI comparison type (e.g. {@code "lastYear"}) + * @param comparisonDirection KPI comparison direction (e.g. {@code "growIsGood"}) + * @param ignoreDashboardFilters list of filters which should be ignored for this KPI (can be empty) + * @param dateDatasetUri KPI date filter dataset URI (optional) + */ + public Kpi(final String title, final String metricUri, final String comparisonType, final String comparisonDirection, + final List ignoreDashboardFilters, final String dateDatasetUri) { + this(new Meta(title), new Content( + notEmpty(metricUri, "metricUri"), + notEmpty(comparisonType, "comparisonType"), + checkDirection(comparisonType, comparisonDirection), + null, + dateDatasetUri, + notNull(ignoreDashboardFilters, "ignoreDashboardFilters"))); + } + + @JsonCreator + private Kpi(@JsonProperty("meta") final Meta meta, @JsonProperty("content") final Content content) { + super(meta); + this.content = content; + } + + private static String checkDirection(final String comparisonType, final String comparisonDirection) { + if (NONE_COMPARISON_TYPE.equalsIgnoreCase(notEmpty(comparisonType, "comparisonType"))) { + return notEmpty(comparisonDirection, "comparisonDirection"); + } else { + return comparisonDirection; + } + } + + /** + * @return KPI metric URI string + */ + @JsonIgnore + public String getMetricUri() { + return getContent().getMetric(); + } + + /** + * @return KPI comparison type + */ + @JsonIgnore + public String getComparisonType() { + return getContent().getComparisonType(); + } + + /** + * @return KPI comparison direction + */ + @JsonIgnore + public String getComparisonDirection() { + return getContent().getComparisonDirection(); + } + + /** + * @return KPI date filter dataset URI + */ + @JsonIgnore + public String getDateDatasetUri() { + final String dateDatasetUri = getContent().getDateDataSet(); + return dateDatasetUri != null ? dateDatasetUri : getContent().getDateDimension(); + } + + /** + * @return list of filter references (containing URIs) of filters which should be ignored for this KPI + */ + @JsonIgnore + public List getIgnoreDashboardFilters() { + return Collections.unmodifiableList(getContent().getIgnoreDashboardFilters()); + } + + @JsonProperty + private Content getContent() { + return content; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Content implements Serializable { + + private final String metric; + private final String comparisonType; + private final String comparisonDirection; + private final String dateDimension; + private final String dateDataset; + private final List ignoreDashboardFilters; + + @JsonCreator + private Content( + @JsonProperty("metric") final String metric, + @JsonProperty("comparisonType") final String comparisonType, + @JsonProperty("comparisonDirection") final String comparisonDirection, + @JsonProperty("dateDimension") final String dateDimension, + @JsonProperty("dateDataSet") final String dateDataset, + @JsonProperty("ignoreDashboardFilters") final List ignoreDashboardFilters) { + this.metric = metric; + this.comparisonType = comparisonType; + this.comparisonDirection = comparisonDirection; + this.dateDimension = dateDimension; + this.dateDataset = dateDataset; + this.ignoreDashboardFilters = ignoreDashboardFilters; + } + + public String getMetric() { + return metric; + } + + public String getComparisonType() { + return comparisonType; + } + + public String getComparisonDirection() { + return comparisonDirection; + } + + /** + * @return URI of the date dimension + * @deprecated if not null, {@link #getDateDataSet()} should be used instead + */ + @Deprecated + public String getDateDimension() { + return dateDimension; + } + + public String getDateDataSet() { + return dateDataset; + } + + public List getIgnoreDashboardFilters() { + return ignoreDashboardFilters; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/KpiAlert.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/KpiAlert.java new file mode 100644 index 000000000..91a762dc5 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/KpiAlert.java @@ -0,0 +1,184 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; + +import java.io.Serializable; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Represents KPI alert set for some KPI on analytical dashboard. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("kpiAlert") +@JsonIgnoreProperties(ignoreUnknown = true) +public class KpiAlert extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = 4771232690549128988L; + + private final Content content; + + /** + * Constructor. + * + * @param title KPI alert title + * @param kpiUri URI of the KPI for which the alert is defined + * @param dashboardUri URI of the KPI where the KPI alert is located + * @param threshold KPI alert threshold + * @param triggerCondition condition for triggering KPI alert + * @param filterContextUri URI of filter context used for computation of KPI alert (optional) + */ + public KpiAlert( + final String title, + final String kpiUri, + final String dashboardUri, + final double threshold, + final String triggerCondition, + final String filterContextUri) { + this(new Meta(title), new Content( + notEmpty(kpiUri, "kpiUri"), + notEmpty(dashboardUri, "dashboardUri"), + threshold, false, + notEmpty(triggerCondition, "triggerCondition"), + filterContextUri)); + } + + @JsonCreator + private KpiAlert(@JsonProperty("meta") final Meta meta, @JsonProperty("content") final Content content) { + super(meta); + this.content = content; + } + + @JsonProperty + private Content getContent() { + return content; + } + + /** + * @return if the KPI alert was already triggered + */ + @JsonIgnore + public boolean wasTriggered() { + return getContent().isTriggered(); + } + + /** + * @return KPI value threshold for triggering KPI alert + */ + @JsonIgnore + public double getThreshold() { + return getContent().getThreshold(); + } + + /** + * @return filters used for computation of KPI alert + */ + @JsonIgnore + public String getFilterContextUri() { + return getContent().getFilterContext(); + } + + /** + * @return condition for triggering KPI alert (e.g. {@code "above_threshold"}) + */ + @JsonIgnore + public String getTriggerCondition() { + return getContent().getWhenTriggered(); + } + + /** + * @return URI of the analytical dashboard where the alert is located + */ + @JsonIgnore + public String getDashboardUri() { + return getContent().getDashboard(); + } + + /** + * @return URI of the KPI for which is the alert configured + */ + @JsonIgnore + public String getKpiUri() { + return getContent().getKpi(); + } + + /** + * Creates new copy of KPI alert with a given triggered state changed. + * + * @param wasTriggered triggered state + * @return new KPI alert + */ + public KpiAlert withTriggeredState(final boolean wasTriggered) { + return new KpiAlert(meta, new Content(getKpiUri(), getDashboardUri(), getThreshold(), wasTriggered, getTriggerCondition(), + getFilterContextUri())); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Content implements Serializable { + + private static final long serialVersionUID = 1L; + + private final boolean isTriggered; + private final double threshold; + private final String filterContext; + private final String whenTriggered; + private final String dashboard; + private final String kpi; + + @JsonCreator + private Content( + @JsonProperty("kpi") final String kpi, + @JsonProperty("dashboard") final String dashboard, + @JsonProperty("threshold") final double threshold, + @JsonProperty("isTriggered") final boolean isTriggered, + @JsonProperty("whenTriggered") final String whenTriggered, + @JsonProperty("filterContext") final String filterContext) { + this.isTriggered = isTriggered; + this.threshold = threshold; + this.filterContext = filterContext; + this.whenTriggered = whenTriggered; + this.dashboard = dashboard; + this.kpi = kpi; + } + + @JsonProperty("isTriggered") + public boolean isTriggered() { + return isTriggered; + } + + public double getThreshold() { + return threshold; + } + + public String getFilterContext() { + return filterContext; + } + + public String getWhenTriggered() { + return whenTriggered; + } + + public String getDashboard() { + return dashboard; + } + + public String getKpi() { + return kpi; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/AttributeFilterReference.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/AttributeFilterReference.java new file mode 100644 index 000000000..b56af6159 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/AttributeFilterReference.java @@ -0,0 +1,47 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Reference for attribute filter for ignoring particular filter in {@link com.gooddata.sdk.model.md.dashboard.Kpi}. + * Is not standalone metadata object - must be part of {@link com.gooddata.sdk.model.md.dashboard.Kpi}. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(AttributeFilterReference.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AttributeFilterReference implements FilterReference { + + static final String NAME = "attributeFilterReference"; + private static final long serialVersionUID = -7882622280867466659L; + private final String displayFormUri; + + /** + * Constructor. + * + * @param displayFormUri display form URI of filter + */ + @JsonCreator + public AttributeFilterReference(@JsonProperty("displayForm") final String displayFormUri) { + this.displayFormUri = notEmpty(displayFormUri, "displayFormUri"); + } + + /** + * @return display form URI of the filter + */ + @JsonProperty("displayForm") + public String getDisplayFormUri() { + return displayFormUri; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardAttributeFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardAttributeFilter.java new file mode 100644 index 000000000..db70fc40c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardAttributeFilter.java @@ -0,0 +1,86 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collections; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Attribute filter located on analytical dashboard. + * Is not standalone metadata object - always must be part of {@link DashboardFilterContext}. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(DashboardAttributeFilter.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class DashboardAttributeFilter implements DashboardFilter { + + static final String NAME = "attributeFilter"; + + private final String displayForm; + private final boolean negativeSelection; + private final List attributeElements; + + /** + * Constructor. + * + * @param displayForm display form of an attribute where this filter is applied + * @param negativeSelection if the negative selection of filter elements is applied + * @param attributeElementUris list of attribute element URIs applied in filter + */ + @JsonCreator + public DashboardAttributeFilter( + @JsonProperty("displayForm") final String displayForm, + @JsonProperty("negativeSelection") final boolean negativeSelection, + @JsonProperty("attributeElements") final List attributeElementUris) { + this.displayForm = notEmpty(displayForm, "displayForm"); + this.negativeSelection = negativeSelection; + this.attributeElements = notEmpty(attributeElementUris, "attributeElementUris"); + } + + /** + * @return display form of an attribute where this filter is applied + */ + public String getDisplayForm() { + return displayForm; + } + + /** + * @return if this filter is negative or positive selection of attribute elements contained in {@link #getAttributeElementUris()} + */ + public boolean isNegativeSelection() { + return negativeSelection; + } + + /** + * @return list of attribute element URI strings which should be included or excluded in filter + * @see #isNegativeSelection() + */ + @JsonIgnore + public List getAttributeElementUris() { + return Collections.unmodifiableList(getAttributeElements()); + } + + @JsonProperty + private List getAttributeElements() { + return attributeElements; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.toString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardDateFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardDateFilter.java new file mode 100644 index 000000000..7dcf10db1 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardDateFilter.java @@ -0,0 +1,147 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Date filter located on analytical dashboard. + * Is not standalone metadata object - always must be part of {@link DashboardFilterContext}. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(DashboardDateFilter.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DashboardDateFilter implements DashboardFilter { + + public static final String RELATIVE_FILTER_TYPE = "relative"; + public static final String ABSOLUTE_FILTER_TYPE = "absolute"; + public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE; + static final String NAME = "dateFilter"; + private static final String ABSOLUTE_DATE_FILTER_GRANULARITY = "GDC.time.date"; + private final String from; + private final String to; + private final String granularity; + private final String dataset; + private final String type; + + @JsonCreator + private DashboardDateFilter( + @JsonProperty("from") final String from, + @JsonProperty("to") final String to, + @JsonProperty("granularity") final String granularity, + @JsonProperty("dataSet") final String dataset, + @JsonProperty("type") final String type) { + this.from = from; + this.to = to; + this.granularity = granularity; + this.dataset = dataset; + this.type = type; + } + + /** + * Creates relative date filter with the given interval and granularity. + * + * @param from interval from + * @param to interval to + * @param granularity granularity (e.g. {@code GDC.time.year}) + * @param datasetUri date dataset URI (optional) + * @return created filter + */ + @JsonIgnore + public static DashboardDateFilter relativeDateFilter(final int from, final int to, final String granularity, final String datasetUri) { + return new DashboardDateFilter( + Integer.toString(from), + Integer.toString(to), + notEmpty(granularity, "granularity"), + datasetUri, + RELATIVE_FILTER_TYPE); + } + + /** + * Creates absolute date filter with the given interval + * + * @param from interval from + * @param to interval to + * @param datasetUri date dataset URI (optional) + * @return created filter + */ + @JsonIgnore + public static DashboardDateFilter absoluteDateFilter(final LocalDate from, final LocalDate to, final String datasetUri) { + return new DashboardDateFilter( + notNull(from, "from").format(DATE_FORMAT), + notNull(to, "to").format(DATE_FORMAT), + ABSOLUTE_DATE_FILTER_GRANULARITY, + datasetUri, + ABSOLUTE_FILTER_TYPE); + } + + /** + * Returns from value of date filter interval. Value represents in the case of: + *

    + *
  • relative date filter - integer value
  • + *
  • absolute date filter - date value with format {@code DD-MM-YYYY}
  • + *
+ * + * @return date filter from interval value + */ + public String getFrom() { + return from; + } + + /** + * Returns to value of date filter interval. Value represents in the case of: + *
    + *
  • relative date filter - integer value
  • + *
  • absolute date filter - date value with format {@code YYYY-DD-MM}
  • + *
+ * + * @return date filter from interval value + */ + public String getTo() { + return to; + } + + /** + * @return date interval granularity in the case of relative date filter + */ + public String getGranularity() { + return granularity; + } + + /** + * @return date dataset URI of this filter + */ + public String getDataSet() { + return dataset; + } + + /** + * @return date filter type, can be either relative or absolute + */ + public String getType() { + return type; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.toString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardFilter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardFilter.java new file mode 100644 index 000000000..ad08662b1 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardFilter.java @@ -0,0 +1,23 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard.filter; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.io.Serializable; + +/** + * Parent interface for filter implementation inside {@link DashboardFilterContext}. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = DashboardDateFilter.class, name = DashboardDateFilter.NAME), + @JsonSubTypes.Type(value = DashboardAttributeFilter.class, name = DashboardAttributeFilter.NAME) +}) +public interface DashboardFilter extends Serializable { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardFilterContext.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardFilterContext.java new file mode 100644 index 000000000..e55ddd92c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DashboardFilterContext.java @@ -0,0 +1,93 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Class encapsulates list of filters on analytical dashboard. + * Currently can contain {@link DashboardDateFilter}s and {@link DashboardAttributeFilter}s. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(DashboardFilterContext.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class DashboardFilterContext extends AbstractObj implements Updatable, Queryable { + + static final String NAME = "filterContext"; + private static final long serialVersionUID = -4572881756272497057L; + private final Content content; + + /** + * Constructs new dashboard filter context with given filters. + * + * @param filters list of dashboard filters (can be empty) + */ + public DashboardFilterContext(final List filters) { + this(new Meta(NAME), new Content(notNull(filters, "filters"))); + } + + @JsonCreator + private DashboardFilterContext(@JsonProperty("meta") final Meta meta, @JsonProperty("content") final Content content) { + super(meta); + this.content = content; + } + + /** + * @return all dashboard filters from this filter context + */ + @JsonIgnore + public List getFilters() { + return Collections.unmodifiableList(getContent().getFilters()); + } + + @JsonProperty + private Content getContent() { + return content; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.toString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Content implements Serializable { + + private final List filters; + + @JsonCreator + private Content(@JsonProperty("filters") final List filters) { + this.filters = filters; + } + + @JsonProperty + private List getFilters() { + return filters; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.toString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DateFilterReference.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DateFilterReference.java new file mode 100644 index 000000000..34520d7c3 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/DateFilterReference.java @@ -0,0 +1,47 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard.filter; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Reference for date filter for ignoring particular filter in {@link com.gooddata.sdk.model.md.dashboard.Kpi}. + * Is not standalone metadata object - must be part of {@link com.gooddata.sdk.model.md.dashboard.Kpi}. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(DateFilterReference.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class DateFilterReference implements FilterReference { + + static final String NAME = "dateFilterReference"; + private static final long serialVersionUID = 6016252592161989340L; + private final String datasetUri; + + /** + * Constructor. + * + * @param datasetUri date dataset URI + */ + @JsonCreator + public DateFilterReference(@JsonProperty("dataSet") final String datasetUri) { + this.datasetUri = notEmpty(datasetUri, "datasetUri"); + } + + /** + * @return date dataset URI of the filter + */ + @JsonProperty("dataSet") + public String getDatasetUri() { + return datasetUri; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/FilterReference.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/FilterReference.java new file mode 100644 index 000000000..c2693917f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/dashboard/filter/FilterReference.java @@ -0,0 +1,23 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +package com.gooddata.sdk.model.md.dashboard.filter; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.io.Serializable; + +/** + * Parent interface for ignored filter references inside {@link com.gooddata.sdk.model.md.dashboard.Kpi}. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = DateFilterReference.class, name = DateFilterReference.NAME), + @JsonSubTypes.Type(value = AttributeFilterReference.class, name = AttributeFilterReference.NAME) +}) +public interface FilterReference extends Serializable { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProject.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProject.java new file mode 100644 index 000000000..0128479e2 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProject.java @@ -0,0 +1,85 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.maintenance; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanStringSerializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Complete project export configuration structure. + * Serialization only. + */ +@JsonTypeName("exportProject") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ExportProject { + + public static final String URI = "/gdc/md/{projectId}/maintenance/export"; + + private final boolean exportUsers; + private final boolean exportData; + private final boolean excludeSchedules; + private final boolean crossDataCenterExport; + private final String authorizedUsers; + + /** + * Creates new ExportProject with default settings. + * Sets exportUsers=true exportData=true, excludeSchedules=false, crossDataCenterExport=false and empty authorizedUsers. + */ + public ExportProject() { + this(true, true, false, false, null); + } + + /** + * Creates new ExportProject. + * + * @param exportUsers whether to add necessary data to be able to clone attribute properties + * @param exportData whether to add necessary data to be able to clone attribute properties + * @param excludeSchedules whether to add necessary data to be able to clone attribute properties + * @param crossDataCenterExport whether export should be usable in any Data Center + * @param authorizedUsers comma-separated list of email addresses of users authorized to import the project, surround email addresses with double quotes + */ + public ExportProject(final boolean exportUsers, final boolean exportData, final boolean excludeSchedules, final boolean crossDataCenterExport, final String authorizedUsers) { + this.exportUsers = exportUsers; + this.exportData = exportData; + this.excludeSchedules = excludeSchedules; + this.crossDataCenterExport = crossDataCenterExport; + this.authorizedUsers = authorizedUsers; + } + + @JsonSerialize(using = BooleanStringSerializer.class) + public boolean isExportUsers() { + return exportUsers; + } + + @JsonSerialize(using = BooleanStringSerializer.class) + public boolean isExportData() { + return exportData; + } + + @JsonSerialize(using = BooleanStringSerializer.class) + public boolean isExcludeSchedules() { + return excludeSchedules; + } + + @JsonSerialize(using = BooleanStringSerializer.class) + public boolean isCrossDataCenterExport() { + return crossDataCenterExport; + } + + public String getAuthorizedUsers() { + return authorizedUsers; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProjectArtifact.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProjectArtifact.java new file mode 100644 index 000000000..2f756a50f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProjectArtifact.java @@ -0,0 +1,25 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.maintenance; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.model.gdc.UriResponse; + +/** + * Complete project export result structure. + * For internal use only. + * Deserialization only. + */ +@JsonTypeName("exportArtifact") +public class ExportProjectArtifact extends PartialMdArtifact { + + @JsonCreator + public ExportProjectArtifact(@JsonProperty("status") UriResponse status, @JsonProperty("token") String token) { + super(status, token); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProjectToken.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProjectToken.java new file mode 100644 index 000000000..3710b44d5 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/ExportProjectToken.java @@ -0,0 +1,43 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.maintenance; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Complete project export token. Serves as configuration structure for import. + * Serialization only. + */ +@JsonTypeName("importProject") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public class ExportProjectToken { + + public static final String URI = "/gdc/md/{projectId}/maintenance/import"; + + private final String token; + + /** + * Creates new ExportProjectToken. + * + * @param token token identifying export of another project + */ + public ExportProjectToken(String token) { + this.token = notEmpty(token, "token"); + } + + public String getToken() { + return token; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdArtifact.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdArtifact.java new file mode 100644 index 000000000..51338b6c2 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdArtifact.java @@ -0,0 +1,53 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.maintenance; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.gdc.UriResponse; + +/** + * Partial metadata export result structure. + * For internal use only. + */ +@JsonTypeName("partialMDArtifact") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class PartialMdArtifact { + + private final UriResponse status; + private final String token; + + @JsonCreator + public PartialMdArtifact(@JsonProperty("status") UriResponse status, @JsonProperty("token") String token) { + this.status = status; + this.token = token; + } + + @JsonIgnore + public String getStatusUri() { + return getStatus().getUri(); + } + + public String getToken() { + return token; + } + + @JsonProperty("status") + private UriResponse getStatus() { + return status; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdExport.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdExport.java new file mode 100644 index 000000000..4a65887f4 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdExport.java @@ -0,0 +1,95 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.maintenance; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; +import java.util.HashSet; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static java.util.Arrays.asList; + +/** + * Partial metadata export configuration structure. + * Serialization only. + */ +@JsonTypeName("partialMDExport") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class PartialMdExport { + + public static final String URI = "/gdc/md/{projectId}/maintenance/partialmdexport"; + + private final Collection uris; + private final boolean crossDataCenterExport; + private final boolean exportAttributeProperties; + + /** + * Creates new PartialMdExport. At least one uri should be given. + * Sets crossDataCenterExport and exportAttributeProperties to false. + * + * @param mdObjectsUris list of uris to metadata objects which should be exported + */ + public PartialMdExport(String... mdObjectsUris) { + this(new HashSet<>(asList(mdObjectsUris))); + } + + /** + * Creates new PartialMdExport. At least one uri should be given. + * Sets crossDataCenterExport and exportAttributeProperties to false. + * + * @param mdObjectsUris list of uris to metadata objects which should be exported + */ + public PartialMdExport(Collection mdObjectsUris) { + this(false, false, mdObjectsUris); + } + + /** + * Creates new PartialMdExport. At least one uri should be given. + * + * @param exportAttributeProperties whether to add necessary data to be able to clone attribute properties + * @param crossDataCenterExport whether export should be usable in any Data Center + * @param mdObjectsUris list of uris to metadata objects which should be exported + */ + public PartialMdExport(boolean exportAttributeProperties, boolean crossDataCenterExport, String... mdObjectsUris) { + this(exportAttributeProperties, crossDataCenterExport, new HashSet<>(asList(mdObjectsUris))); + } + + /** + * Creates new PartialMdExport. At least one uri should be given. + * + * @param exportAttributeProperties whether to add necessary data to be able to clone attribute properties + * @param crossDataCenterExport whether export should be usable in any Data Center + * @param mdObjectsUris list of uris to metadata objects which should be exported + */ + public PartialMdExport(boolean exportAttributeProperties, boolean crossDataCenterExport, Collection mdObjectsUris) { + notEmpty(mdObjectsUris, "uris"); + this.uris = mdObjectsUris; + this.crossDataCenterExport = crossDataCenterExport; + this.exportAttributeProperties = exportAttributeProperties; + } + + public Collection getUris() { + return uris; + } + + public boolean isCrossDataCenterExport() { + return crossDataCenterExport; + } + + public boolean isExportAttributeProperties() { + return exportAttributeProperties; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdExportToken.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdExportToken.java new file mode 100644 index 000000000..9f267a6c9 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/maintenance/PartialMdExportToken.java @@ -0,0 +1,120 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.maintenance; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Partial metadata export token. Serves as configuration structure for import. + * Serialization only. + */ +@JsonTypeName("partialMDImport") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public class PartialMdExportToken { + + public static final String URI = "/gdc/md/{projectId}/maintenance/partialmdimport"; + + private final String token; + private boolean overwriteNewer; + private boolean updateLDMObjects; + private boolean importAttributeProperties; + + /** + * Creates new PartialMdExportToken. + *
+ * Sets default values to properties: + *
    + *
  • importAttributeProperties - default false
  • + *
  • overwriteNewer - default true
  • + *
  • updateLDMObjects - default false
  • + *
+ * + * @param token token identifying metadata partially exported from another project + */ + public PartialMdExportToken(String token) { + this(token, false); + } + + /** + * Creates new PartialMdExportToken. For internal purposes only. + *
+ * Sets default values to properties: + *
    + *
  • overwriteNewer - default true
  • + *
  • updateLDMObjects - default false
  • + *
+ * + * @param token token identifying metadata partially exported from another project + * @param importAttributeProperties see {@link #setImportAttributeProperties(boolean)} + */ + public PartialMdExportToken(String token, boolean importAttributeProperties) { + this.token = notEmpty(token, "token"); + setImportAttributeProperties(importAttributeProperties); + setOverwriteNewer(true); + setUpdateLDMObjects(false); + } + + public String getToken() { + return token; + } + + public boolean isOverwriteNewer() { + return overwriteNewer; + } + + /** + * Sets the flag {@code overwriteNewer}. + * If {@code true}, UDM/ADM objects are overwritten without checking modification time. + * + * @param overwriteNewer flag value to be set + */ + public void setOverwriteNewer(boolean overwriteNewer) { + this.overwriteNewer = overwriteNewer; + } + + public boolean isUpdateLDMObjects() { + return updateLDMObjects; + } + + /** + * Sets the flag {@code updateLDMObjects}. + * If {@code true}, related LDM objects name, description and tags are overwritten + * + * @param updateLDMObjects flag value to be set + */ + public void setUpdateLDMObjects(boolean updateLDMObjects) { + this.updateLDMObjects = updateLDMObjects; + } + + public boolean isImportAttributeProperties() { + return importAttributeProperties; + } + + /** + * Sets the flag {@code importAttributeProperties}. + * If {@code true}, following attribute properties are cloned: + *
    + *
  • for attribute - import drillDownStepAttributeDF setting
  • + *
  • for attributeDisplayForm - import type setting
  • + *
+ * It also implies {@code 'updateLDMObjects = true'} for all mentioned types.
+ * It will not reliably work (can fail) for exports without {@code 'exportAttributeProperties = true'}. + * + * @param importAttributeProperties flag value to be set + */ + public void setImportAttributeProperties(boolean importAttributeProperties) { + this.importAttributeProperties = importAttributeProperties; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/AttributeInGrid.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/AttributeInGrid.java new file mode 100644 index 000000000..47a418433 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/AttributeInGrid.java @@ -0,0 +1,142 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Attribute; +import com.gooddata.sdk.model.md.DisplayForm; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Attribute in Grid + */ +@JsonTypeName("attribute") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AttributeInGrid implements GridElement, Serializable { + + private static final long serialVersionUID = 9061580138440068825L; + private final String uri; + private final String alias; + private final List> totals; + + @JsonCreator + AttributeInGrid(@JsonProperty("uri") String uri, @JsonProperty("totals") List> totals, + @JsonProperty("alias") String alias) { + this.uri = uri; + this.alias = alias; + this.totals = totals; + } + + /** + * Creates new instance. + * + * @param uri uri of displayForm of attribute to be in grid + * @param alias alias used to label the attribute + */ + public AttributeInGrid(String uri, String alias) { + this(uri, new ArrayList<>(), alias); + } + + /** + * Creates new instance. + * + * @param uri uri of displayForm of attribute to be in grid + * @param alias alias used to label the attribute + * @param totals totals for metrics used in grid - for each {@link MetricElement} in grid, there can be list + * of totals. The totals are evaluated in given order. + */ + public AttributeInGrid(String uri, String alias, List> totals) { + this(uri, alias); + notNull(totals, "totals"); + for (List totalList : totals) { + final List totalStringList = new ArrayList<>(totalList.size()); + totalStringList.addAll(totalList.stream().map(Total::toString).collect(Collectors.toList())); + this.totals.add(totalStringList); + } + } + + /** + * Creates new AttributeInGrid using given DisplayForm's uri and it's title as alias. + * + * @param displayForm displayForm to create AttributeInGrid from + */ + public AttributeInGrid(final DisplayForm displayForm) { + this(notNull(notNull(displayForm, "displayForm").getUri(), "uri"), notNull(displayForm, "displayForm").getTitle()); + } + + /** + * Creates new AttributeInGrid using given DisplayForm's uri and given alias. + * + * @param displayForm displayForm to create AttributeInGrid from + * @param alias alias used to label the attribute + */ + public AttributeInGrid(final DisplayForm displayForm, final String alias) { + this(notNull(notNull(displayForm, "displayForm").getUri(), "uri"), alias); + } + + /** + * Creates new AttributeInGrid using given Attribute's default DisplayForm's uri and Attribute's title as alias. + * + * @param attribute attribute to create AttributeInGrid from + */ + public AttributeInGrid(final Attribute attribute) { + this(notNull(attribute, "attribute").getDefaultDisplayForm(), notNull(attribute, "attribute").getTitle()); + } + + /** + * Creates new AttributeInGrid using given Attribute's default DisplayForm's uri and given alias. + * + * @param attribute attribute to create AttributeInGrid from + * @param alias alias used to label the attribute + */ + public AttributeInGrid(final Attribute attribute, final String alias) { + this(notNull(attribute, "attribute").getDefaultDisplayForm(), alias); + } + + @JsonProperty("totals") + public List> getStringTotals() { + return totals; + } + + @JsonIgnore + public List> getTotals() { + final List> enumTotals = new ArrayList<>(totals.size()); + for (List totalList : totals) { + final List totalEnumList = new ArrayList<>(totalList.size()); + totalEnumList.addAll(totalList.stream().map(Total::of).collect(Collectors.toList())); + enumTotals.add(totalEnumList); + } + return enumTotals; + } + + public String getUri() { + return uri; + } + + public String getAlias() { + return alias; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Filter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Filter.java new file mode 100644 index 000000000..d66f42d46 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Filter.java @@ -0,0 +1,41 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Filter (in report definition) + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Filter implements Serializable { + + private static final long serialVersionUID = 5776136459952322123L; + private final String expression; + + @JsonCreator + public Filter(@JsonProperty("expression") final String expression) { + this.expression = notEmpty(expression, "expression"); + } + + public String getExpression() { + return expression; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Grid.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Grid.java new file mode 100644 index 000000000..b1da3b11a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Grid.java @@ -0,0 +1,98 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Grid content (in report definition) + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Grid implements Serializable { + + private static final long serialVersionUID = -1274604506924273761L; + private final List columns; + private final List rows; + private final List metrics; + private final Map> sort; + private final Collection> columnWidths; + + /** + * Creates new instance. + * + * @param columns report's definition columns + * @param rows report's definition rows + * @param metrics report's definition metrics + * @param sort report's sort definition + * @param columnWidths report columns' widths definition + * @since 2.0.0 + */ + @JsonCreator + public Grid(@JsonProperty("columns") @JsonDeserialize(contentUsing = GridElementDeserializer.class) + List columns, + @JsonProperty("rows") @JsonDeserialize(contentUsing = GridElementDeserializer.class) + List rows, + @JsonProperty("metrics") List metrics, + @JsonProperty("sort") Map> sort, + @JsonProperty("columnWidths") Collection> columnWidths) { + this.columns = columns; + this.rows = rows; + this.metrics = metrics; + this.sort = sort; + this.columnWidths = columnWidths; + } + + public Grid(List columns, List rows, List metrics) { + this.columns = columns; + this.rows = rows; + this.metrics = metrics; + sort = new LinkedHashMap<>(); + sort.put("columns", Collections.emptyList()); + sort.put("rows", Collections.emptyList()); + columnWidths = Collections.emptyList(); + } + + @JsonSerialize(contentUsing = GridElementSerializer.class) + public List getColumns() { + return columns; + } + + @JsonSerialize(contentUsing = GridElementSerializer.class) + public List getRows() { + return rows; + } + + public List getMetrics() { + return metrics; + } + + public Collection> getColumnWidths() { + return columnWidths; + } + + public Map> getSort() { + return sort; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElement.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElement.java new file mode 100644 index 000000000..7fd1e3b2f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElement.java @@ -0,0 +1,14 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +/** + * Grid element + * (metricGroup | AttributeInGrid ... metricGroup can be maximally + * one time in rows or columns) + */ +public interface GridElement { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElementDeserializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElementDeserializer.java new file mode 100644 index 000000000..ec4856d41 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElementDeserializer.java @@ -0,0 +1,39 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; + +import static com.gooddata.sdk.model.md.report.MetricGroup.METRIC_GROUP; + +/** + * Custom deserializer for {@link GridElement}'s implementations + */ +class GridElementDeserializer extends JsonDeserializer { + + @Override + public GridElement deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + switch (jp.currentToken()) { + case VALUE_STRING: + final String textValue = ctxt.readValue(jp, String.class); + if (MetricGroup.equals(textValue)) { + return METRIC_GROUP; + } else { + return (GridElement) ctxt.handleWeirdStringValue(GridElement.class, textValue, + "Unknown string representation of GridElement: %s", textValue); + } + case START_OBJECT: + return ctxt.readValue(jp, AttributeInGrid.class); + default: + return (GridElement) ctxt.handleUnexpectedToken(GridElement.class, jp.currentToken(), jp, + "Unknown type of GridElement"); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElementSerializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElementSerializer.java new file mode 100644 index 000000000..58276c1ab --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridElementSerializer.java @@ -0,0 +1,30 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +/** + * Custom serializer for {@link GridElement}'s implementations + */ +class GridElementSerializer extends JsonSerializer { + + @Override + public void serialize(GridElement value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value instanceof AttributeInGrid) { + serializers.defaultSerializeValue(value, gen); + } else if (value instanceof MetricGroup) { + gen.writeString(((MetricGroup) value).getValue()); + } else { + throw new JsonGenerationException("Unsupported kind of GridElement: " + value.getClass().getName(), gen); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridReportDefinitionContent.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridReportDefinitionContent.java new file mode 100644 index 000000000..44a0998eb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/GridReportDefinitionContent.java @@ -0,0 +1,55 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.md.Meta; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Grid report definition + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class GridReportDefinitionContent extends ReportDefinitionContent { + + public static final String FORMAT = "grid"; + private static final long serialVersionUID = 1296467241365069724L; + + @JsonCreator + private GridReportDefinitionContent( + @JsonProperty("format") String format, + @JsonProperty("grid") Grid grid, + @JsonProperty("filters") Collection filters) { + super(format, grid, filters); + } + + GridReportDefinitionContent(Grid grid, Collection filters) { + this(FORMAT, grid, filters); + } + + GridReportDefinitionContent(Grid grid) { + this(grid, Collections.emptyList()); + } + + public static ReportDefinition create(String title, List columns, List rows, + List metrics) { + return create(title, columns, rows, metrics, Collections.emptyList()); + } + + public static ReportDefinition create(String title, List columns, List rows, + List metrics, Collection filters) { + return new ReportDefinition(new Meta(title), new GridReportDefinitionContent(new Grid(columns, rows, metrics), filters)); + } + + public String getFormat() { + return FORMAT; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/MetricElement.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/MetricElement.java new file mode 100644 index 000000000..b2889f52f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/MetricElement.java @@ -0,0 +1,83 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Metric; + +import java.io.Serializable; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Metric used in {@link Grid} for report definition. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MetricElement implements Serializable { + + private static final long serialVersionUID = 6199743301553304055L; + private final String uri; + private final String alias; + private final String format; + private final String drillAcrossStepAttributeDisplayFormUri; + + @JsonCreator + MetricElement(@JsonProperty("uri") final String uri, + @JsonProperty("alias") final String alias, + @JsonProperty("format") final String format, + @JsonProperty("drillAcrossStepAttributeDF") String drillAcrossStepAttributeDisplayFormUri) { + this.uri = uri; + this.alias = alias; + this.format = format; + this.drillAcrossStepAttributeDisplayFormUri = drillAcrossStepAttributeDisplayFormUri; + } + + /** + * Creates new instance using uri of given metric and alias. + * + * @param metric metric to create element from + * @param alias metric alias + */ + public MetricElement(final Metric metric, final String alias) { + this(notNull(notNull(metric, "metric").getUri(), "uri"), notNull(alias, "alias"), null, null); + } + + /** + * Creates new instance using uri of given metric and it's title as alias. + * + * @param metric metric to create element from + */ + public MetricElement(final Metric metric) { + this(metric, notNull(metric, "metric").getTitle()); + } + + public String getUri() { + return uri; + } + + public String getAlias() { + return alias; + } + + public String getFormat() { + return format; + } + + @JsonProperty("drillAcrossStepAttributeDF") + public String getDrillAcrossStepAttributeDisplayFormUri() { + return drillAcrossStepAttributeDisplayFormUri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/MetricGroup.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/MetricGroup.java new file mode 100644 index 000000000..398f06b50 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/MetricGroup.java @@ -0,0 +1,51 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import java.io.Serializable; + +/** + * Marker element marking the placement of metrics in Grid report. + * Can be contained either in rows or columns of {@link Grid}. + */ +public final class MetricGroup implements GridElement, Serializable { + + private static final long serialVersionUID = -2971228185501817988L; + private static final String JSON_VALUE = "metricGroup"; + public static final MetricGroup METRIC_GROUP = new MetricGroup(JSON_VALUE); + private final String value; + + private MetricGroup(String value) { + this.value = value; + } + + /** + * @param string string to compare whether is metricGroup + * @return true when the {@link #METRIC_GROUP}'s string value equals to the argument, false otherwise + */ + public static boolean equals(String string) { + return METRIC_GROUP.getValue().equals(string); + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return getValue() != null ? getValue().hashCode() : 0; + } + + @Override + public String toString() { + return getValue(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/OneNumberReportDefinitionContent.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/OneNumberReportDefinitionContent.java new file mode 100644 index 000000000..543535218 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/OneNumberReportDefinitionContent.java @@ -0,0 +1,92 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.md.Meta; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * One number report definition + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class OneNumberReportDefinitionContent extends ReportDefinitionContent { + + public static final String FORMAT = "oneNumber"; + private static final long serialVersionUID = 5479509323034916986L; + private final OneNumberVisualization oneNumber; + + @JsonCreator + private OneNumberReportDefinitionContent(@JsonProperty("format") String format, @JsonProperty("grid") Grid grid, + @JsonProperty("oneNumber") OneNumberVisualization oneNumber, + @JsonProperty("filters") Collection filters) { + super(format, grid, filters); + this.oneNumber = oneNumber; + } + + /* Just for serialization test */ + OneNumberReportDefinitionContent(Grid grid, String description, Collection filters) { + super(FORMAT, grid, filters); + oneNumber = new OneNumberVisualization(new OneNumberLabels(description)); + } + + public static ReportDefinition create(String title, List columns, List rows, + List metrics) { + return create(title, columns, rows, metrics, Collections.emptyList()); + } + + public static ReportDefinition create(String title, List columns, List rows, + List metrics, Collection filters) { + return new ReportDefinition(new Meta(title), new OneNumberReportDefinitionContent( + new Grid(columns, rows, metrics), title, filters)); + } + + public String getFormat() { + return FORMAT; + } + + public OneNumberVisualization getOneNumber() { + return oneNumber; + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class OneNumberVisualization implements Serializable { + + private static final long serialVersionUID = 1105233720917978784L; + private final OneNumberLabels labels; + + @JsonCreator + private OneNumberVisualization(@JsonProperty("labels") final OneNumberLabels labels) { + this.labels = labels; + } + + public OneNumberLabels getLabels() { + return labels; + } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class OneNumberLabels implements Serializable { + + private static final long serialVersionUID = 6464599509495095669L; + private final String description; + + @JsonCreator + private OneNumberLabels(@JsonProperty("description") String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Report.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Report.java new file mode 100644 index 000000000..a1b4ff81c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Report.java @@ -0,0 +1,97 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; + +import static java.util.Collections.emptyList; + +/** + * Report + */ +@JsonTypeName("report") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Report extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = 189440633045112981L; + + @JsonProperty("content") + private final Content content; + + @JsonCreator + private Report(@JsonProperty("meta") Meta meta, @JsonProperty("content") Content content) { + super(meta); + this.content = content; + } + + /** + * Creates new report with given title and definitions + * + * @param title report title + * @param definitions report definitions + */ + public Report(String title, ReportDefinition... definitions) { + // domains must be empty list to be included in serialized form properly + this(new Meta(title), new Content(Arrays.asList(uris(definitions)), emptyList())); + } + + @JsonIgnore + public Collection getDefinitions() { + return content.getDefinitions(); + } + + @JsonIgnore + public Collection getDomains() { + return content.getDomains(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + private static class Content implements Serializable { + + private static final long serialVersionUID = 3437452191500594445L; + private final Collection definitions; + private final Collection domains; + + @JsonCreator + public Content(@JsonProperty("definitions") Collection definitions, + @JsonProperty("domains") Collection domains) { + this.definitions = definitions; + this.domains = domains; + } + + public Collection getDefinitions() { + return definitions; + } + + public Collection getDomains() { + return domains; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/ReportDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/ReportDefinition.java new file mode 100644 index 000000000..9ab2ed74c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/ReportDefinition.java @@ -0,0 +1,78 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; + +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notNullState; + +/** + * Report definition + */ +@JsonTypeName("reportDefinition") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ReportDefinition extends AbstractObj implements Queryable, Updatable { + + private static final long serialVersionUID = 9069611345896541468L; + private static final String EXPLAIN_LINK = "explain2"; + + @JsonProperty("content") + private final ReportDefinitionContent content; + + @JsonProperty("links") + private final Map links; + + @JsonCreator + ReportDefinition(@JsonProperty("meta") Meta meta, @JsonProperty("content") ReportDefinitionContent content, @JsonProperty("links") Map links) { + super(meta); + this.content = content; + this.links = links; + } + + ReportDefinition(Meta meta, ReportDefinitionContent content) { + this(meta, content, null); + } + + /* Just for serialization test */ + ReportDefinition(String title, ReportDefinitionContent content) { + super(new Meta(title)); + this.content = content; + this.links = null; + } + + @JsonIgnore + public String getFormat() { + return content.getFormat(); + } + + @JsonIgnore + public Grid getGrid() { + return content.getGrid(); + } + + @JsonIgnore + public String getExplainUri() { + return notNullState(links, "links").get(EXPLAIN_LINK); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/ReportDefinitionContent.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/ReportDefinitionContent.java new file mode 100644 index 000000000..4a993006f --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/ReportDefinitionContent.java @@ -0,0 +1,58 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.io.Serializable; +import java.util.Collection; + +/** + * Report definition content + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "format") +@JsonSubTypes({ + @JsonSubTypes.Type(name = GridReportDefinitionContent.FORMAT, value = GridReportDefinitionContent.class), + @JsonSubTypes.Type(name = OneNumberReportDefinitionContent.FORMAT, + value = OneNumberReportDefinitionContent.class) + //TODO chart +}) +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class ReportDefinitionContent implements Serializable { + + private static final long serialVersionUID = -9027442077911353550L; + private final String format; + private final Grid grid; + private final Collection filters; + + public ReportDefinitionContent(String format, Grid grid, Collection filters) { + this.format = format; + this.grid = grid; + this.filters = filters; + } + + @JsonIgnore // handled by type info + public String getFormat() { + return format; + } + + public Grid getGrid() { + return grid; + } + + public Collection getFilters() { + return filters; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Total.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Total.java new file mode 100644 index 000000000..04ee28a92 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/report/Total.java @@ -0,0 +1,57 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.report; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; +import java.util.List; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.joining; + +/** + * Represents type of Total for {@link AttributeInGrid} + */ +public enum Total { + SUM, + AVG, + MAX, + MIN, + NAT, + MED; + + @JsonCreator + public static Total of(String total) { + notNull(total, "total"); + try { + return Total.valueOf(total.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException( + format("Unknown value for Grid's total: \"%s\", supported values are: [%s]", + total, stream(Total.values()).map(Enum::name).collect(joining(","))), + e); + } + } + + /** + * Ordered list of totals + * + * @return ordered list of totals which refers to order of totals data returned by the executor in executionResult + */ + public static List orderedValues() { + return Arrays.asList(SUM, MAX, MIN, AVG, MED, NAT); + } + + @JsonValue + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/Bucket.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/Bucket.java new file mode 100644 index 000000000..0757f9fdd --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/Bucket.java @@ -0,0 +1,108 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.executeafm.afm.LocallyIdentifiable; +import com.gooddata.sdk.model.executeafm.resultspec.TotalItem; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * Represents bucket within {@link VisualizationObject} + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Bucket implements Serializable, LocallyIdentifiable { + + private static final long serialVersionUID = -7718720886547680021L; + private final String localIdentifier; + private final List items; + private final List totals; + + /** + * Creates new instance of bucket without totals + * + * @param localIdentifier local identifier of bucket + * @param items list of {@link BucketItem}s for this bucket + */ + public Bucket(@JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("items") final List items) { + this(localIdentifier, items, null); + } + + /** + * Creates new instance of bucket + * + * @param localIdentifier local identifier of bucket + * @param items list of {@link BucketItem}s for this bucket + * @param totals list of {@link TotalItem}s for this bucket + */ + @JsonCreator + public Bucket(@JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("items") final List items, + @JsonProperty("totals") List totals) { + this.localIdentifier = localIdentifier; + this.items = items; + this.totals = totals; + } + + /** + * @return local identifier + */ + public String getLocalIdentifier() { + return localIdentifier; + } + + /** + * @return list of {@link BucketItem}s + */ + public List getItems() { + return items; + } + + /** + * @return list of defined {@link TotalItem}s + */ + public List getTotals() { + return totals; + } + + @JsonIgnore + VisualizationAttribute getOnlyAttribute() { + if (getItems() != null && getItems().size() == 1) { + final BucketItem item = getItems().iterator().next(); + if (item instanceof VisualizationAttribute) { + return (VisualizationAttribute) item; + } + } + + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Bucket bucket = (Bucket) o; + return Objects.equals(localIdentifier, bucket.localIdentifier) + && Objects.equals(items, bucket.items) + && Objects.equals(totals, bucket.totals); + } + + @Override + public int hashCode() { + return Objects.hash(localIdentifier, items, totals); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/BucketItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/BucketItem.java new file mode 100644 index 000000000..c61170c3c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/BucketItem.java @@ -0,0 +1,25 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Interface for bucket items within {@link Bucket} + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = Measure.class, name = Measure.NAME), + @JsonSubTypes.Type(value = VisualizationAttribute.class, name = VisualizationAttribute.NAME) +}) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public interface BucketItem { +} + diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/CollectionType.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/CollectionType.java new file mode 100644 index 000000000..fc6d05502 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/CollectionType.java @@ -0,0 +1,20 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +/** + * Represents type of collection that can be used as local identifier for {@link Bucket} + */ +public enum CollectionType { + SEGMENT, + STACK, + TREND, + VIEW; + + boolean isValueOf(final String type) { + return name().toLowerCase().equals(type.toLowerCase()); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/Measure.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/Measure.java new file mode 100644 index 000000000..0c396cd75 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/Measure.java @@ -0,0 +1,100 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.executeafm.afm.MeasureDefinition; +import com.gooddata.sdk.model.executeafm.afm.MeasureItem; + +import java.util.Objects; + +/** + * Represents measure item within {@link Bucket} + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Measure extends MeasureItem implements BucketItem { + + static final String NAME = "measure"; + private static final long serialVersionUID = -6311373783004640731L; + private String title; + + /** + * Creates new instance of measure for use in {@link VisualizationObject} + * + * @param definition measure definition + * @param localIdentifier local identifier + */ + public Measure(final MeasureDefinition definition, final String localIdentifier) { + super(definition, localIdentifier); + } + + /** + * Creates new instance of measure for use in {@link VisualizationObject} + * + * @param definition measure definition + * @param localIdentifier local identifier + * @param alias alias for measure title + * @param title default name given to measure + * @param format format of measure to be computed + */ + @JsonCreator + public Measure(@JsonProperty("definition") final MeasureDefinition definition, + @JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("alias") final String alias, + @JsonProperty("title") final String title, + @JsonProperty("format") final String format) { + super(definition, localIdentifier, alias, format); + this.title = title; + } + + /** + * @return true if measure definition has compute ratio set to true, false otherwise + */ + @SuppressWarnings("deprecation") + @JsonIgnore + public boolean hasComputeRatio() { + return getDefinition() instanceof VOSimpleMeasureDefinition && ((VOSimpleMeasureDefinition) getDefinition()).hasComputeRatio(); + } + + /** + * @return title of measure + */ + public String getTitle() { + return title; + } + + /** + * @param title of measure + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * @return true if measure contains {@link VOPopMeasureDefinition}, false otherwise + */ + @SuppressWarnings("deprecation") + @JsonIgnore + public boolean isPop() { + return getDefinition() instanceof VOPopMeasureDefinition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Measure measure = (Measure) o; + return super.equals(measure); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VOPopMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VOPopMeasureDefinition.java new file mode 100644 index 000000000..63a53e4cd --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VOPopMeasureDefinition.java @@ -0,0 +1,43 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.afm.PopMeasureDefinition; + +import static com.gooddata.sdk.model.md.visualization.VOPopMeasureDefinition.NAME; + +/** + * Period over Period measure definition to be used within {@link Measure} + * + * @deprecated identical with {@link PopMeasureDefinition}, see https://github.com/gooddata/gooddata-java/issues/581 + * Let's remove it once it's removed from API. + */ +@Deprecated +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonRootName(NAME) +public class VOPopMeasureDefinition extends PopMeasureDefinition { + + public static final String NAME = "popMeasureDefinition"; + private static final long serialVersionUID = -2727004914980057124L; + + /** + * Creates instance of Period over Period measure definition to be used in {@link VisualizationObject} + * + * @param measureIdentifier reference to local identifier of {@link VOSimpleMeasureDefinition} over which is PoP calculated + * @param popAttribute uri to attribute used for PoP + */ + @JsonCreator + public VOPopMeasureDefinition(@JsonProperty("measureIdentifier") final String measureIdentifier, + @JsonProperty("popAttribute") final ObjQualifier popAttribute) { + super(measureIdentifier, popAttribute); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VOSimpleMeasureDefinition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VOSimpleMeasureDefinition.java new file mode 100644 index 000000000..3b117b507 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VOSimpleMeasureDefinition.java @@ -0,0 +1,80 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.gooddata.sdk.model.executeafm.ObjQualifier; +import com.gooddata.sdk.model.executeafm.afm.Aggregation; +import com.gooddata.sdk.model.executeafm.afm.MeasureDefinition; +import com.gooddata.sdk.model.executeafm.afm.SimpleMeasureDefinition; +import com.gooddata.sdk.model.executeafm.afm.filter.FilterItem; + +import java.util.List; + +import static com.gooddata.sdk.model.md.visualization.VOSimpleMeasureDefinition.NAME; + +/** + * Simple measure definition to be used within {@link Measure} + * + * @deprecated identical with {@link MeasureDefinition}, see https://github.com/gooddata/gooddata-java/issues/581 + * Let's remove it once it's removed from API. + */ +@Deprecated +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonRootName(NAME) +public class VOSimpleMeasureDefinition extends SimpleMeasureDefinition { + + public static final String NAME = "measureDefinition"; + private static final long serialVersionUID = 8467311354259963694L; + + /** + * Creates instance of simple measure definition to be used in {@link VisualizationObject} + * + * @see SimpleMeasureDefinition#SimpleMeasureDefinition(ObjQualifier) + */ + public VOSimpleMeasureDefinition(ObjQualifier item) { + super(item); + } + + /** + * Creates instance of simple measure definition to be used in {@link VisualizationObject} + * + * @param item uri to measure + * @param aggregation used aggregation function + * @param computeRatio indicates if result should be calculated in percents + * @param filters filters by which measure is filtered + */ + @JsonCreator + public VOSimpleMeasureDefinition(@JsonProperty("item") final ObjQualifier item, + @JsonProperty("aggregation") final String aggregation, + @JsonProperty("computeRatio") final Boolean computeRatio, + @JsonProperty("filters") final List filters) { + super(item, aggregation, computeRatio, filters); + } + + /** + * Creates instance of simple measure definition to be used in {@link VisualizationObject} + * + * @see SimpleMeasureDefinition#SimpleMeasureDefinition(ObjQualifier, Aggregation, Boolean, List) + */ + public VOSimpleMeasureDefinition(ObjQualifier item, Aggregation aggregation, Boolean computeRatio, + List filters) { + super(item, aggregation, computeRatio, filters); + } + + /** + * Creates instance of simple measure definition to be used in {@link VisualizationObject} + * + * @see SimpleMeasureDefinition#SimpleMeasureDefinition(ObjQualifier, Aggregation, Boolean, FilterItem...) + */ + public VOSimpleMeasureDefinition(ObjQualifier item, Aggregation aggregation, Boolean computeRatio, + FilterItem... filters) { + super(item, aggregation, computeRatio, filters); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationAttribute.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationAttribute.java new file mode 100644 index 000000000..d0b74a437 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationAttribute.java @@ -0,0 +1,61 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; +import com.gooddata.sdk.model.executeafm.afm.AttributeItem; + +import java.util.Objects; + +/** + * Represents attribute item withing {@link Bucket} + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VisualizationAttribute extends AttributeItem implements BucketItem { + + static final String NAME = "visualizationAttribute"; + private static final long serialVersionUID = -5144496152695494774L; + + /** + * Creates new instance of visualization attribute for use in {@link Bucket} + * + * @param displayForm display form of attribute + * @param localIdentifier local identifier of attribute + */ + public VisualizationAttribute(final UriObjQualifier displayForm, final String localIdentifier) { + super(displayForm, localIdentifier); + } + + /** + * Creates new instance of visualization attribute for use in {@link Bucket} + * + * @param displayForm display form of attribute + * @param localIdentifier local identifier of attribute + * @param alias alias of attribute + */ + @JsonCreator + public VisualizationAttribute(@JsonProperty("displayForm") final UriObjQualifier displayForm, + @JsonProperty("localIdentifier") final String localIdentifier, + @JsonProperty("alias") final String alias) { + super(displayForm, localIdentifier, alias); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + VisualizationAttribute attribute = (VisualizationAttribute) o; + return super.equals(attribute); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationClass.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationClass.java new file mode 100644 index 000000000..497c37195 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationClass.java @@ -0,0 +1,151 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; + +import java.io.Serializable; + +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.WRAPPER_OBJECT; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; +import static org.apache.commons.lang3.Validate.notNull; + +/** + * Class for holding information about visualization, including uri to its implementation, icons, order index and checksum + */ +@JsonTypeName(VisualizationClass.NAME) +@JsonTypeInfo(include = WRAPPER_OBJECT, use = NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VisualizationClass extends AbstractObj implements Queryable, Updatable { + + static final String NAME = "visualizationClass"; + private static final long serialVersionUID = -72785788784079208L; + private Content content; + + private VisualizationClass(@JsonProperty("content") final Content content, @JsonProperty("meta") final Meta meta) { + super(meta); + this.content = notNull(content); + } + + /** + * @return default icon for visualization, for local visualizations in form of 'local:{@link VisualizationType}' + */ + @JsonIgnore + public String getIcon() { + return getContent().getIcon(); + } + + /** + * @return icon displayed on mouse over, for local visualizations in form of 'local:{@link VisualizationType}.selected' + */ + @JsonIgnore + public String getIconSelected() { + return getContent().getIconSelected(); + } + + /** + * @return checksum of visualization class, 'local' for local visualizations + */ + @JsonIgnore + public String getChecksum() { + return getContent().getChecksum(); + } + + /** + * @return absolute position of icon in list + */ + @JsonIgnore + public Float getOrderIndex() { + return getContent().getOrderIndex(); + } + + /** + * @return uri to implementation of visualization, for local visualizations in form of 'local:{@link VisualizationType}' + */ + @JsonIgnore + public String getUrl() { + return getContent().getUrl(); + } + + /** + * @return type of visualization, table by default + */ + @JsonIgnore + public VisualizationType getVisualizationType() { + VisualizationType visualizationType = VisualizationType.TABLE; + + String uriParts[] = getContent().getUrl().split(":"); + + if (uriParts.length > 0 && isLocal()) { + String derivedType = uriParts[uriParts.length - 1]; + + visualizationType = VisualizationType.of(derivedType); + } + + return visualizationType; + } + + @JsonIgnore + private boolean isLocal() { + return getContent().getChecksum().equals("local"); + } + + private Content getContent() { + return content; + } + + private static class Content implements Serializable { + private final String url; + private final String icon; + private final String iconSelected; + private final String checksum; + private final Float orderIndex; + + @JsonCreator + private Content(@JsonProperty("url") String url, + @JsonProperty("icon") String icon, + @JsonProperty("iconSelected") String iconSelected, + @JsonProperty("checksum") String checksum, + @JsonProperty("orderIndex") Float orderIndex) { + this.url = notNull(url); + this.icon = notNull(icon); + this.iconSelected = notNull(iconSelected); + this.checksum = notNull(checksum); + this.orderIndex = orderIndex; + } + + public String getUrl() { + return url; + } + + public String getIcon() { + return icon; + } + + public String getIconSelected() { + return iconSelected; + } + + public String getChecksum() { + return checksum; + } + + public Float getOrderIndex() { + return orderIndex; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationConverter.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationConverter.java new file mode 100644 index 000000000..b02e5b7aa --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationConverter.java @@ -0,0 +1,476 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gooddata.sdk.model.executeafm.Execution; +import com.gooddata.sdk.model.executeafm.afm.Afm; +import com.gooddata.sdk.model.executeafm.afm.AttributeItem; +import com.gooddata.sdk.model.executeafm.afm.MeasureItem; +import com.gooddata.sdk.model.executeafm.afm.NativeTotalItem; +import com.gooddata.sdk.model.executeafm.afm.SimpleMeasureDefinition; +import com.gooddata.sdk.model.executeafm.afm.filter.CompatibilityFilter; +import com.gooddata.sdk.model.executeafm.afm.filter.DateFilter; +import com.gooddata.sdk.model.executeafm.afm.filter.ExtendedFilter; +import com.gooddata.sdk.model.executeafm.afm.filter.FilterItem; +import com.gooddata.sdk.model.executeafm.afm.filter.MeasureValueFilter; +import com.gooddata.sdk.model.executeafm.afm.filter.NegativeAttributeFilter; +import com.gooddata.sdk.model.executeafm.afm.filter.PositiveAttributeFilter; +import com.gooddata.sdk.model.executeafm.afm.filter.RankingFilter; +import com.gooddata.sdk.model.executeafm.resultspec.Dimension; +import com.gooddata.sdk.model.executeafm.resultspec.ResultSpec; +import com.gooddata.sdk.model.executeafm.resultspec.SortItem; +import com.gooddata.sdk.model.executeafm.resultspec.TotalItem; +import com.gooddata.sdk.model.md.report.Total; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.gooddata.sdk.common.util.Validate.isTrue; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.model.executeafm.resultspec.Dimension.MEASURE_GROUP; +import static java.util.stream.Collectors.toList; + +/** + * Helper class for converting {@link VisualizationObject} into {@link Execution} + */ +public abstract class VisualizationConverter { + private static final ObjectMapper MAPPER = new ObjectMapper(); + + /** + * Generate Execution from Visualization object. + *

+ * NOTE: totals are not included in this conversion + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClassGetter {@link Function} for fetching VisualizationClass, + * which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + * @see #convertToExecution(VisualizationObject, VisualizationClass) + */ + public static Execution convertToExecution(final VisualizationObject visualizationObject, + final Function visualizationClassGetter) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClassGetter, "visualizationClassGetter"); + return convertToExecution(visualizationObject, + visualizationClassGetter.apply(visualizationObject.getVisualizationClassUri())); + } + + /** + * Generate Execution from Visualization object. + *

+ * NOTE: totals are not included in this conversion + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClass visualizationClass, which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + * @see #convertToAfm(VisualizationObject) + * @see #convertToResultSpec(VisualizationObject, VisualizationClass) + */ + public static Execution convertToExecution(final VisualizationObject visualizationObject, + final VisualizationClass visualizationClass) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClass, "visualizationClass"); + ResultSpec resultSpec = convertToResultSpec(visualizationObject, visualizationClass); + Afm afm = convertToAfm(visualizationObject); + return new Execution(afm, resultSpec); + } + + /** + * Generate Execution from Visualization object with totals included. + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClassGetter {@link Function} for fetching VisualizationClass, + * which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + * @see #convertToExecutionWithTotals(VisualizationObject, VisualizationClass) + */ + public static Execution convertToExecutionWithTotals(final VisualizationObject visualizationObject, + final Function visualizationClassGetter) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClassGetter, "visualizationClassGetter"); + return convertToExecutionWithTotals(visualizationObject, + visualizationClassGetter.apply(visualizationObject.getVisualizationClassUri())); + } + + /** + * Generate Execution from Visualization object with totals included. + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClass visualizationClass, which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + * @see #convertToAfmWithNativeTotals(VisualizationObject) + * @see #convertToResultSpecWithTotals(VisualizationObject, VisualizationClass) + */ + public static Execution convertToExecutionWithTotals(final VisualizationObject visualizationObject, + final VisualizationClass visualizationClass) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClass, "visualizationClass"); + ResultSpec resultSpec = convertToResultSpecWithTotals(visualizationObject, visualizationClass); + Afm afm = convertToAfmWithNativeTotals(visualizationObject); + return new Execution(afm, resultSpec); + } + + /** + * Generate Afm from Visualization object. + *

+ * NOTE: native totals are not included in this conversion + * + * @param visualizationObject which will be converted to {@link Execution} + * @return {@link Afm} object + */ + public static Afm convertToAfm(final VisualizationObject visualizationObject) { + notNull(visualizationObject, "visualizationObject"); + final VisualizationObject visualizationObjectWithoutTotals = removeTotals(visualizationObject); + return convertToAfmWithNativeTotals(visualizationObjectWithoutTotals); + } + + /** + * Generate Afm from Visualization object with native totals included. + * + * @param visualizationObject which will be converted to {@link Execution} + * @return {@link Afm} object + */ + public static Afm convertToAfmWithNativeTotals(final VisualizationObject visualizationObject) { + notNull(visualizationObject, "visualizationObject"); + final List attributes = convertAttributes(visualizationObject.getAttributes()); + final List filters = convertFilters(visualizationObject.getFilters()); + final List measures = convertMeasures(visualizationObject.getMeasures()); + final List totals = convertNativeTotals(visualizationObject); + + return new Afm(attributes, filters, measures, totals); + } + + /** + * Generate ResultSpec from Visualization object. Currently {@link ResultSpec}'s {@link Dimension}s can be generated + * for table and four types of chart: bar, column, line and pie. + *

+ * NOTE: totals are not included in this conversion + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClassGetter {@link Function} for fetching VisualizationClass, + * which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + */ + public static ResultSpec convertToResultSpec(final VisualizationObject visualizationObject, + final Function visualizationClassGetter) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClassGetter, "visualizationClassGetter"); + return convertToResultSpec(visualizationObject, + visualizationClassGetter.apply(visualizationObject.getVisualizationClassUri())); + } + + /** + * Generate ResultSpec from Visualization object. Currently {@link ResultSpec}'s {@link Dimension}s can be generated + * for table and four types of chart: bar, column, line and pie. + *

+ * NOTE: totals are not included in this conversion + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClass VisualizationClass, which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + */ + public static ResultSpec convertToResultSpec(final VisualizationObject visualizationObject, + final VisualizationClass visualizationClass) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClass, "visualizationClass"); + final VisualizationObject visualizationObjectWithoutTotals = removeTotals(visualizationObject); + return convertToResultSpecWithTotals(visualizationObjectWithoutTotals, visualizationClass); + } + + /** + * Generate ResultSpec from Visualization object with totals included. Currently {@link ResultSpec}'s {@link Dimension}s + * can be generated for table and four types of chart: bar, column, line and pie. + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClassGetter {@link Function} for fetching VisualizationClass, + * which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + */ + public static ResultSpec convertToResultSpecWithTotals(final VisualizationObject visualizationObject, + final Function visualizationClassGetter) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClassGetter, "visualizationClassGetter"); + return convertToResultSpecWithTotals(visualizationObject, + visualizationClassGetter.apply(visualizationObject.getVisualizationClassUri())); + } + + /** + * Generate ResultSpec from Visualization object with totals included. Currently {@link ResultSpec}'s {@link Dimension}s + * can be generated for table and four types of chart: bar, column, line and pie. + * + * @param visualizationObject which will be converted to {@link Execution} + * @param visualizationClass VisualizationClass, which is necessary for correct generation of {@link ResultSpec} + * @return {@link Execution} object + */ + public static ResultSpec convertToResultSpecWithTotals(final VisualizationObject visualizationObject, + final VisualizationClass visualizationClass) { + notNull(visualizationObject, "visualizationObject"); + notNull(visualizationClass, "visualizationClass"); + isTrue(visualizationObject.getVisualizationClassUri().equals(visualizationClass.getUri()), + "visualizationClass URI does not match the URI within visualizationObject, " + + "you're trying to create ResultSpec for incompatible objects"); + List sorts = getSorting(visualizationObject); + List dimensions = getDimensions(visualizationObject, visualizationClass.getVisualizationType()); + return new ResultSpec(dimensions, sorts); + } + + static List getSorting(final VisualizationObject visualizationObject) { + try { + List sorts = parseSorting(visualizationObject.getProperties()); + if (sorts != null) { + return sorts; + } + } catch (Exception ignored) { + } + + return null; + } + + static List parseSorting(final String properties) throws Exception { + JsonNode jsonProperties = parseProperties(properties); + JsonNode nodeSortItems = jsonProperties.get("sortItems"); + TypeReference> mapType = new TypeReference>() { + }; + return MAPPER.convertValue(nodeSortItems, mapType); + } + + /** + * Creates a new {@link VisualizationObject} derived from the original one, with all "totals" removed from its buckets. + * This is to ensure backward compatibility in cases where totals were not previously handled. + * + * @param visualizationObject original {@link VisualizationObject} + * @return a new VisualizationObject derived from the original but without any totals in the buckets. + */ + private static VisualizationObject removeTotals(final VisualizationObject visualizationObject) { + final List bucketsWithoutTotals = visualizationObject.getBuckets().stream() + // create buckets without totals + .map(bucket -> new Bucket(bucket.getLocalIdentifier(), bucket.getItems())) + .collect(toList()); + return visualizationObject.withBuckets(bucketsWithoutTotals); + } + + private static List getDimensions(final VisualizationObject visualizationObject, + final VisualizationType visualizationType) { + switch (visualizationType) { + case COLUMN: + case BAR: + return getDimensionsForStacked(visualizationObject, CollectionType.STACK, CollectionType.VIEW); + case LINE: + return getDimensionsForStacked(visualizationObject, CollectionType.SEGMENT, CollectionType.TREND); + case PIE: + return getDimensionsForPie(visualizationObject); + default: + return getDimensionsForTable(visualizationObject); + } + } + + private static List getDimensionsForPie(final VisualizationObject visualizationObject) { + VisualizationAttribute attribute = visualizationObject.getView(); + + List dimensions = new ArrayList<>(); + + if (visualizationObject.hasMeasures()) { + dimensions.add(new Dimension(MEASURE_GROUP)); + } + + if (attribute != null) { + dimensions.add(new Dimension(attribute.getLocalIdentifier())); + } + + return dimensions; + } + + private static List getDimensionsForStacked(final VisualizationObject visualizationObject, + final CollectionType stackBy, + final CollectionType viewBy) { + VisualizationAttribute stack = visualizationObject.getAttributeFromCollection(stackBy); + VisualizationAttribute view = visualizationObject.getAttributeFromCollection(viewBy); + + List dimensions = new ArrayList<>(); + + if (stack != null) { + dimensions.add(new Dimension(stack.getLocalIdentifier())); + + List dimensionItems = new ArrayList<>(); + + if (view != null) { + dimensionItems.add(view.getLocalIdentifier()); + } + + if (visualizationObject.hasMeasures()) { + dimensionItems.add(MEASURE_GROUP); + } + + if (!dimensionItems.isEmpty()) { + dimensions.add(new Dimension(dimensionItems)); + } + } else { + if (visualizationObject.hasMeasures()) { + dimensions.add(new Dimension(MEASURE_GROUP)); + } + + if (view != null) { + dimensions.add(new Dimension(view.getLocalIdentifier())); + } + } + + return dimensions; + } + + private static List getDimensionsForTable(final VisualizationObject visualizationObject) { + List dimensions = new ArrayList<>(); + + List attributes = visualizationObject.getAttributes(); + List totals = visualizationObject.getTotals(); + + if (!attributes.isEmpty()) { + final Dimension attributeDimension = new Dimension(attributes.stream() + .map(VisualizationAttribute::getLocalIdentifier) + .collect(toList())); + if (!totals.isEmpty()) { + attributeDimension.setTotals(new HashSet<>(totals)); + } + dimensions.add(attributeDimension); + } else { + dimensions.add(new Dimension(new ArrayList<>())); + } + + if (visualizationObject.hasMeasures()) { + dimensions.add(new Dimension(MEASURE_GROUP)); + } + + return dimensions; + } + + private static JsonNode parseProperties(final String properties) throws Exception { + return MAPPER.readValue(properties, JsonNode.class); + } + + private static List convertAttributes(final List attributes) { + return attributes.stream() + .map(AttributeItem.class::cast) + .collect(toList()); + } + + private static List convertFilters(final List filters) { + if (filters == null) { + return new ArrayList<>(); + } + + List validFilters = removeIrrelevantFilters(filters); + return getCompatibilityFilters(validFilters); + } + + private static List convertMeasures(final List measures) { + return measures.stream() + .map(VisualizationConverter::removeFormat) + .map(VisualizationConverter::getAfmMeasure) + .map(VisualizationConverter::convertMeasureFilters) + .collect(toList()); + } + + private static Measure removeFormat(final Measure measure) { + if (measure.hasComputeRatio()) { + measure.setFormat(""); + } + + return measure; + } + + private static MeasureItem getAfmMeasure(final Measure measure) { + String alias = measure.getAlias(); + String usedTitle = alias; + if (alias == null || alias.isEmpty()) { + if (measure.getTitle() != null) { + usedTitle = measure.getTitle(); + } + } + return new MeasureItem(measure.getDefinition(), measure.getLocalIdentifier(), usedTitle, measure.getFormat()); + } + + private static MeasureItem convertMeasureFilters(final MeasureItem measure) { + if (measure.getDefinition() instanceof SimpleMeasureDefinition) { + List filters = ((SimpleMeasureDefinition) measure.getDefinition()).getFilters(); + + List validFilters; + if (filters == null) { + validFilters = new ArrayList<>(); + } else { + validFilters = removeIrrelevantFilters(filters); + } + + ((SimpleMeasureDefinition) measure.getDefinition()).setFilters(validFilters); + } + + return measure; + } + + private static List getCompatibilityFilters(final List filters) { + return filters.stream() + .map(CompatibilityFilter.class::cast) + .collect(toList()); + } + + private static List removeIrrelevantFilters(final List filters) { + return filters.stream() + .filter(f -> { + if (f instanceof DateFilter) { + return !((DateFilter) f).isAllTimeSelected(); + } else if (f instanceof MeasureValueFilter) { + return ((MeasureValueFilter) f).getCondition() != null; + } else if (f instanceof NegativeAttributeFilter) { + return !((NegativeAttributeFilter) f).isAllSelected(); + } else return f instanceof PositiveAttributeFilter || f instanceof RankingFilter; + + }) + .collect(Collectors.toList()); + } + + private static List convertNativeTotals(final VisualizationObject visualizationObject) { + final List attributeBuckets = getAttributeBuckets(visualizationObject); + final List attributeIds = getIdsFromAttributeBuckets(attributeBuckets); + return attributeBuckets.stream() + .filter(bucket -> bucket.getTotals() != null) + .flatMap(bucket -> bucket.getTotals().stream()) + .filter(totalItem -> isNativeTotal(totalItem) && attributeIds.contains(totalItem.getAttributeIdentifier())) + .map(totalItem -> convertToNativeTotalItem(totalItem, attributeIds)) + .collect(toList()); + } + + private static NativeTotalItem convertToNativeTotalItem(TotalItem totalItem, List attributeIds) { + final int attributeIdx = attributeIds.indexOf(totalItem.getAttributeIdentifier()); + return new NativeTotalItem( + totalItem.getMeasureIdentifier(), + new ArrayList<>(attributeIds.subList(0, attributeIdx)) + ); + } + + private static List getAttributeBuckets(final VisualizationObject visualizationObject) { + return visualizationObject.getBuckets().stream() + .filter(bucket -> bucket.getItems().stream().allMatch(AttributeItem.class::isInstance)) + .collect(toList()); + } + + private static List getIdsFromAttributeBuckets(final List attributeBuckets) { + return attributeBuckets.stream() + .flatMap(bucket -> + bucket.getItems().stream() + .map(AttributeItem.class::cast) + .map(AttributeItem::getLocalIdentifier) + ) + .collect(toList()); + } + + private static boolean isNativeTotal(TotalItem totalItem) { + return totalItem.getType() != null && Total.NAT.name().equals(totalItem.getType().toUpperCase()); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationObject.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationObject.java new file mode 100644 index 000000000..dea99a829 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationObject.java @@ -0,0 +1,463 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.model.executeafm.Execution; +import com.gooddata.sdk.model.executeafm.UriObjQualifier; +import com.gooddata.sdk.model.executeafm.afm.Afm; +import com.gooddata.sdk.model.executeafm.afm.filter.ExtendedFilter; +import com.gooddata.sdk.model.executeafm.resultspec.ResultSpec; +import com.gooddata.sdk.model.executeafm.resultspec.TotalItem; +import com.gooddata.sdk.model.md.AbstractObj; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.md.Obj; +import com.gooddata.sdk.model.md.Queryable; +import com.gooddata.sdk.model.md.Updatable; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.WRAPPER_OBJECT; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; +import static org.apache.commons.lang3.Validate.notEmpty; +import static org.apache.commons.lang3.Validate.notNull; + +/** + * Complete information about new visualization object that can be stored as MD object (see {@link Obj}) + * to md server. + *

+ * The visualization object is part of new GD UI visualizations situated in AD and KPI dashboards. + * This object is a persistent form of AFM (Attribute, Measures, Filters) report executions. + */ +@JsonTypeName(VisualizationObject.NAME) +@JsonTypeInfo(include = WRAPPER_OBJECT, use = NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VisualizationObject extends AbstractObj implements Queryable, Updatable { + static final String NAME = "visualizationObject"; + + @JsonProperty("content") + private final Content content; + + /** + * Constructor. + * + * @param title title of visualization object + * @param visualizationClassUri uri to the {@link VisualizationClass} + */ + public VisualizationObject(final String title, final String visualizationClassUri) { + this(new Content(notEmpty(visualizationClassUri), emptyList()), new Meta(notEmpty(title))); + } + + @JsonCreator + private VisualizationObject(@JsonProperty("content") final Content content, @JsonProperty("meta") final Meta meta) { + super(meta); + this.content = notNull(content); + } + + /** + * @return all measures from all buckets in visualization object + */ + @JsonIgnore + public List getMeasures() { + return content.getMeasures(); + } + + /** + * @return all totals from all buckets in visualization object + */ + @JsonIgnore + public List getTotals() { + return content.getTotals(); + } + + /** + * Get measure by local identifier or null if not found + * + * @param localIdentifier of measure + * @return measure or null + */ + @JsonIgnore + public Measure getMeasure(final String localIdentifier) { + return getMeasures().stream() + .filter(measure -> measure.getLocalIdentifier().equals(localIdentifier)) + .findFirst() + .orElse(null); + } + + /** + * @return all measures from all buckets whose measure definition is instance of {@link VOSimpleMeasureDefinition} + */ + @SuppressWarnings("deprecation") + @JsonIgnore + public List getSimpleMeasures() { + return getMeasures().stream() + .filter(measure -> measure.getDefinition() instanceof VOSimpleMeasureDefinition) + .collect(toList()); + } + + /** + * @return all attributes from all buckets in visualization object + */ + @JsonIgnore + public List getAttributes() { + return content.getAttributes(); + } + + @JsonIgnore + public VisualizationAttribute getAttribute(final String localIdentifier) { + return getAttributes().stream() + .filter(attribute -> attribute.getLocalIdentifier().equals(localIdentifier)) + .findFirst() + .orElse(null); + } + + /** + * Returns attribute from collection bucket, if and only if bucket contains exactly one item of type + * {@link VisualizationAttribute}, null otherwise + * + * @param type of collection which we want to get, stored as local identifier in each bucket + * @return attribute from collection bucket + */ + @JsonIgnore + public VisualizationAttribute getAttributeFromCollection(final CollectionType type) { + return content.getBuckets().stream() + .filter(bucket -> type.isValueOf(bucket.getLocalIdentifier())) + .findFirst() + .map(Bucket::getOnlyAttribute) + .orElse(null); + } + + @JsonIgnore + public boolean hasMeasures() { + return !getMeasures().isEmpty(); + } + + /** + * @return true if visualization object contains at leas one PoP measure or measure with compute ratio, false otherwise + */ + @JsonIgnore + public boolean hasDerivedMeasure() { + return getMeasures().stream().anyMatch(measure -> measure.isPop() || measure.hasComputeRatio()); + } + + /** + * Method to get uri to requested local identifier from reference items + * + * @param id of item + * @return uri of requested item + */ + @JsonIgnore + public String getItemById(String id) { + Map referenceItems = getReferenceItems(); + if (referenceItems != null) { + return referenceItems.get(id); + } + + return null; + } + + /** + * @return attribute from stack collection + */ + @JsonIgnore + public VisualizationAttribute getStack() { + return getAttributeFromCollection(CollectionType.STACK); + } + + /** + * @return attribute from view collection + */ + @JsonIgnore + public VisualizationAttribute getView() { + return getAttributeFromCollection(CollectionType.VIEW); + } + + /** + * @return attribute from segment collection + */ + @JsonIgnore + public VisualizationAttribute getSegment() { + return getAttributeFromCollection(CollectionType.SEGMENT); + } + + /** + * @return attribute from trend collection + */ + @JsonIgnore + public VisualizationAttribute getTrend() { + return getAttributeFromCollection(CollectionType.TREND); + } + + /** + * @return uri to the {@link VisualizationClass} + */ + @JsonIgnore + public String getVisualizationClassUri() { + return content.getVisualizationClassUri(); + } + + /** + * @return buckets from visualization object + */ + @JsonIgnore + public List getBuckets() { + return content.getBuckets(); + } + + /** + * @param buckets replacing previous visualization object's buckets + */ + @JsonIgnore + public void setBuckets(List buckets) { + content.setBuckets(buckets); + } + + /** + * @return a new copy of this {@link VisualizationObject} with the specified buckets + */ + @JsonIgnore + public VisualizationObject withBuckets(List buckets) { + return new VisualizationObject(content.withBuckets(buckets), meta); + } + + /** + * @return filters from visualization object + */ + @JsonIgnore + public List getFilters() { + return content.getFilters(); + } + + /** + * @param filters replacing previous visualization object's filters + */ + @JsonIgnore + public void setFilters(List filters) { + content.setFilters(filters); + } + + /** + * @return json properties of visualization object in form of string + */ + @JsonIgnore + public String getProperties() { + return content.getProperties(); + } + + /** + * @param properties to be set to visualization object in form of stringified json + */ + @JsonIgnore + public void setProperties(String properties) { + content.setProperties(properties); + } + + /** + * @return hash map of references in form localIdentifier:uri + */ + @JsonIgnore + public Map getReferenceItems() { + return content.getReferenceItems(); + } + + /** + * @param referenceItems is a hash map of references in form localIdentifier:uri to be set to visualization object + */ + @JsonIgnore + public void setReferenceItems(Map referenceItems) { + content.setReferenceItems(referenceItems); + } + + /** + * @return uri to visualization class wrapped as {@link UriObjQualifier} + */ + @JsonIgnore + public UriObjQualifier getVisualizationClass() { + return content.getVisualizationClass(); + } + + /** + * @param uri to replace previous visualization class's uri, wrapped as {@link UriObjQualifier} + */ + @JsonIgnore + public void setVisualizationClass(UriObjQualifier uri) { + content.setVisualizationClass(uri); + } + + /** + * @see VisualizationConverter#convertToExecution(VisualizationObject, Function) + */ + @JsonIgnore + public Execution convertToExecution(Function visualizationClassgetter) { + return VisualizationConverter.convertToExecution(this, visualizationClassgetter); + } + + /** + * @see VisualizationConverter#convertToExecution(VisualizationObject, VisualizationClass) + */ + @JsonIgnore + public Execution convertToExecution(VisualizationClass visualizationClass) { + return VisualizationConverter.convertToExecution(this, visualizationClass); + } + + /** + * @see VisualizationConverter#convertToAfm(VisualizationObject) + */ + @JsonIgnore + public Afm convertToAfm() { + return VisualizationConverter.convertToAfm(this); + } + + /** + * @see VisualizationConverter#convertToResultSpec(VisualizationObject, Function) + */ + @JsonIgnore + public ResultSpec convertToResultSpec(Function visualizationClassgetter) { + return VisualizationConverter.convertToResultSpec(this, visualizationClassgetter); + } + + /** + * @see VisualizationConverter#convertToResultSpec(VisualizationObject, VisualizationClass) + */ + @JsonIgnore + public ResultSpec convertToResultSpec(VisualizationClass visualizationClass) { + return VisualizationConverter.convertToResultSpec(this, visualizationClass); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Content implements Serializable { + + private static final long serialVersionUID = 2895359822118041504L; + private UriObjQualifier visualizationClass; + private List buckets; + private List filters; + private String properties; + private Map referenceItems; + + private Content(final String visualizationClassUri, final List buckets) { + this(new UriObjQualifier(visualizationClassUri), buckets, null, null, null); + } + + @JsonCreator + private Content(@JsonProperty("visualizationClass") final UriObjQualifier visualizationClass, + @JsonProperty("buckets") final List buckets, + @JsonProperty("filters") final List filters, + @JsonProperty("properties") final String properties, + @JsonProperty("references") final Map referenceItems) { + + this.visualizationClass = notNull(visualizationClass); + this.buckets = notNull(buckets); + this.filters = filters; + this.properties = properties; + this.referenceItems = referenceItems; + } + + public List getBuckets() { + return buckets; + } + + public void setBuckets(List buckets) { + this.buckets = buckets; + } + + @JsonIgnore + public List getAttributes() { + return buckets.stream() + .flatMap(bucket -> bucket.getItems().stream()) + .filter(VisualizationAttribute.class::isInstance) + .map(VisualizationAttribute.class::cast) + .collect(toList()); + } + + @JsonIgnore + public List getMeasures() { + return buckets.stream() + .flatMap(bucket -> bucket.getItems().stream()) + .filter(Measure.class::isInstance) + .map(Measure.class::cast) + .collect(toList()); + } + + @JsonIgnore + public List getTotals() { + return buckets.stream() + .filter(bucket -> bucket.getTotals() != null) + .flatMap(bucket -> bucket.getTotals().stream()) + .collect(toList()); + } + + @JsonIgnore + public String getVisualizationClassUri() { + return visualizationClass.getUri(); + } + + public List getFilters() { + if (filters == null) { + return new ArrayList<>(); + } + + return filters; + } + + public void setFilters(List filters) { + this.filters = filters; + } + + public String getProperties() { + return properties; + } + + public void setProperties(String properties) { + this.properties = properties; + } + + public UriObjQualifier getVisualizationClass() { + return visualizationClass; + } + + public void setVisualizationClass(UriObjQualifier visualizationClass) { + this.visualizationClass = visualizationClass; + } + + @JsonProperty("references") + public Map getReferenceItems() { + return referenceItems; + } + + public void setReferenceItems(Map referenceItems) { + this.referenceItems = referenceItems; + } + + /** + * @return a new copy of this {@link Content} with the specified buckets + */ + @JsonIgnore + public Content withBuckets(List buckets) { + return new Content( + visualizationClass, + buckets, + filters != null ? new ArrayList<>(filters) : null, + properties, + referenceItems != null ? new HashMap<>(referenceItems) : null + ); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationType.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationType.java new file mode 100644 index 000000000..c9d9794ee --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/md/visualization/VisualizationType.java @@ -0,0 +1,33 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.md.visualization; + +import static com.gooddata.sdk.common.util.Validate.notNull; +import static java.lang.String.format; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.joining; + +/** + * Represents supported types of currently used visualizations + */ +public enum VisualizationType { + TABLE, + LINE, + COLUMN, + BAR, + PIE; + + public static VisualizationType of(final String type) { + try { + return VisualizationType.valueOf(notNull(type, "type").toUpperCase()); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException( + format("Unknown visualization type: \"%s\", supported types are: [%s]", + type, stream(VisualizationType.values()).map(Enum::name).collect(joining(","))), + e); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Channel.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Channel.java new file mode 100644 index 000000000..73c98949d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Channel.java @@ -0,0 +1,62 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.account.Account; +import com.gooddata.sdk.model.md.Meta; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Notification channel + */ +@JsonTypeName("channelConfiguration") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Channel { + + public static final String URI = Account.URI + "/channelConfigurations"; + + private final Configuration configuration; + private final Meta meta; + + public Channel(final Configuration configuration, + final String title) { + this( + notNull(configuration, "configuration"), + new Meta( + notEmpty(title, "title") + ) + ); + } + + @JsonCreator + private Channel(@JsonProperty("configuration") final Configuration configuration, + @JsonProperty("meta") final Meta meta) { + this.configuration = configuration; + this.meta = meta; + } + + public Configuration getConfiguration() { + return configuration; + } + + public Meta getMeta() { + return meta; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Configuration.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Configuration.java new file mode 100644 index 000000000..e470fa1c1 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Configuration.java @@ -0,0 +1,20 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Interface for configurations of channel + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.WRAPPER_OBJECT) +@JsonSubTypes({ + @JsonSubTypes.Type(value = EmailConfiguration.class, name = "emailConfiguration"), +}) +public interface Configuration { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/EmailConfiguration.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/EmailConfiguration.java new file mode 100644 index 000000000..3828c2e9a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/EmailConfiguration.java @@ -0,0 +1,39 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Email configuration of channel + */ +@JsonTypeName("emailConfiguration") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class EmailConfiguration implements Configuration { + private final String to; + + @JsonCreator + public EmailConfiguration(@JsonProperty("to") final String to) { + this.to = notEmpty(to, "to"); + } + + public String getTo() { + return to; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/MessageTemplate.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/MessageTemplate.java new file mode 100644 index 000000000..b7141cb1b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/MessageTemplate.java @@ -0,0 +1,40 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Template of notification message + */ +@JsonTypeName("template") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class MessageTemplate { + + private final String expression; + + @JsonCreator + public MessageTemplate(@JsonProperty("expression") final String expression) { + this.expression = notEmpty(expression, "expression"); + } + + public String getExpression() { + return expression; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/ProjectEvent.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/ProjectEvent.java new file mode 100644 index 000000000..2cad800a0 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/ProjectEvent.java @@ -0,0 +1,88 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.HashMap; +import java.util.Map; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * Project event. + *

+ * Serialization only. + */ +@JsonTypeName("projectEvent") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +public class ProjectEvent { + + public static final String URI = "/gdc/projects/{projectId}/notifications/events"; + + private final String type; + private final Map parameters; + + public ProjectEvent(final String type) { + this(type, new HashMap<>()); + } + + public ProjectEvent(final String type, final Map parameters) { + notEmpty(type, "type"); + notNull(parameters, "parameters"); + this.type = type; + this.parameters = parameters; + } + + /** + * Set the parameter with given key and value, overwriting the preceding value of the same key (if any). + * + * @param key parameter key + * @param value parameter value + */ + public void setParameter(final String key, final String value) { + notNull(key, "parameter key"); + notNull(value, "parameter value"); + parameters.put(key, value); + } + + @JsonProperty("type") + public String getType() { + return type; + } + + @JsonProperty("parameters") + public Map getParameters() { + return parameters; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ProjectEvent that = (ProjectEvent) o; + + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return parameters != null ? parameters.equals(that.parameters) : that.parameters == null; + } + + @Override + public int hashCode() { + int result = type != null ? type.hashCode() : 0; + result = 31 * result + (parameters != null ? parameters.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Subscription.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Subscription.java new file mode 100644 index 000000000..456b08a4b --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Subscription.java @@ -0,0 +1,132 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Meta; + +import java.util.List; +import java.util.stream.Collectors; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +import static org.apache.commons.lang3.Validate.notEmpty; +import static org.apache.commons.lang3.Validate.notNull; + +/** + * Subscription for notifications + */ +@JsonTypeName("subscription") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(NON_NULL) +public class Subscription { + + public static final String URI = "/gdc/projects/{project}/users/{user}/subscriptions"; + + private final List triggers; + private final TriggerCondition condition; + private final MessageTemplate messageTemplate; + private final MessageTemplate subjectTemplate; + private final List channels; + private final Meta meta; + + + /** + * Creates Subscription + * + * @param triggers triggers of subscription + * @param channels list of {@link Channel} + * @param condition condition under which this subscription activates + * @param messageTemplate of message + * @param title name of subscription + */ + public Subscription(final List triggers, + final List channels, + final TriggerCondition condition, + final MessageTemplate messageTemplate, + final String title) { + this(triggers, channels, condition, messageTemplate, null, title); + } + + /** + * Creates Subscription + * + * @param triggers triggers of subscription + * @param channels list of {@link Channel} + * @param condition condition under which this subscription activates + * @param messageTemplate of message + * @param subjectTemplate of message + * @param title name of subscription + */ + public Subscription(final List triggers, + final List channels, + final TriggerCondition condition, + final MessageTemplate messageTemplate, + final MessageTemplate subjectTemplate, + final String title) { + this(notNull(triggers, "triggers"), + notNull(condition, "condition"), + notNull(messageTemplate, "messageTemplate"), + subjectTemplate, + notNull(channels, "channels").stream().map(e -> e.getMeta().getUri()).collect(Collectors.toList()), + new Meta( + notEmpty(title, "title") + ) + ); + } + + @JsonCreator + Subscription(@JsonProperty("triggers") final List triggers, + @JsonProperty("condition") final TriggerCondition condition, + @JsonProperty("message") final MessageTemplate messageTemplate, + @JsonProperty("subject") final MessageTemplate subjectTemplate, + @JsonProperty("channels") final List channels, + @JsonProperty("meta") final Meta meta) { + this.triggers = triggers; + this.condition = condition; + this.messageTemplate = messageTemplate; + this.subjectTemplate = subjectTemplate; + this.channels = channels; + this.meta = meta; + } + + public List getTriggers() { + return triggers; + } + + public TriggerCondition getCondition() { + return condition; + } + + @JsonProperty("message") + public MessageTemplate getTemplate() { + return messageTemplate; + } + + @JsonProperty("subject") + public MessageTemplate getSubjectTemplate() { + return subjectTemplate; + } + + public List getChannels() { + return channels; + } + + public Meta getMeta() { + return meta; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/TimerEvent.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/TimerEvent.java new file mode 100644 index 000000000..3c42b9eee --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/TimerEvent.java @@ -0,0 +1,40 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Time based trigger + */ +@JsonTypeName("timerEvent") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class TimerEvent implements Trigger { + + private final String cronExpression; + + @JsonCreator + public TimerEvent(@JsonProperty("cronExpression") final String cronExpression) { + this.cronExpression = notEmpty(cronExpression, "cronExpression"); + } + + public String getCronExpression() { + return cronExpression; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Trigger.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Trigger.java new file mode 100644 index 000000000..42da4afd4 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/Trigger.java @@ -0,0 +1,20 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Interface for subscription triggers + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.WRAPPER_OBJECT) +@JsonSubTypes({ + @JsonSubTypes.Type(value = TimerEvent.class, name = "timerEvent"), +}) +public interface Trigger { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/TriggerCondition.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/TriggerCondition.java new file mode 100644 index 000000000..5bfe26932 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/notification/TriggerCondition.java @@ -0,0 +1,34 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.notification; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Condition of trigger + */ +@JsonTypeName("condition") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class TriggerCondition { + + private final String expression; + + @JsonCreator + public TriggerCondition(@JsonProperty("expression") final String expression) { + this.expression = notEmpty(expression, "expression"); + } + + public String getExpression() { + return expression; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/CreatedInvitations.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/CreatedInvitations.java new file mode 100644 index 000000000..aa2f7e613 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/CreatedInvitations.java @@ -0,0 +1,71 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collections; +import java.util.List; + +/** + * Created invitations + */ +@JsonTypeName("createdInvitations") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class CreatedInvitations { + + private final List invitationUris; + private final List domainMismatchEmails; + private final List alreadyInProjectEmails; + + @JsonCreator + private CreatedInvitations(@JsonProperty("uri") List invitationUris, + @JsonProperty("loginsDomainMismatch") List domainMismatchEmails, + @JsonProperty("loginsAlreadyInProject") List alreadyInProjectEmails) { + this.invitationUris = invitationUris != null ? invitationUris : Collections.emptyList(); + this.domainMismatchEmails = domainMismatchEmails != null ? domainMismatchEmails : Collections.emptyList(); + this.alreadyInProjectEmails = alreadyInProjectEmails != null ? alreadyInProjectEmails : Collections.emptyList(); + } + + /** + * List of successful invitations + * + * @return list of URIs (never null) + */ + public List getInvitationUris() { + return invitationUris; + } + + /** + * List of emails which can't be invited to the project because their users belongs to a different domain + * + * @return list of emails (never null) + */ + public List getDomainMismatchEmails() { + return domainMismatchEmails; + } + + /** + * List of emails which can't be invited to the project because they are already members of the project + * + * @return list of emails (never null) + */ + public List getAlreadyInProjectEmails() { + return alreadyInProjectEmails; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Environment.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Environment.java new file mode 100644 index 000000000..a30b12a5c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Environment.java @@ -0,0 +1,25 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +/** + * Optional property for project or warehouse create, the property is ignored during update. + * Default value is {@link #PRODUCTION} which is also environment for all currently existing projects or warehouses. + */ +public enum Environment { + /** + * Default value, projects or warehouses are backed-up and archived. + */ + PRODUCTION, + /** + * no meaning yet and behavior is the same as for {@link #PRODUCTION} projects or warehouses. + */ + DEVELOPMENT, + /** + * 'TESTING' projects or warehouses are not backed-up and archived. + */ + TESTING +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Invitation.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Invitation.java new file mode 100644 index 000000000..d496ba39c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Invitation.java @@ -0,0 +1,80 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Meta; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +/** + * Project invitation + */ +@JsonTypeName("invitation") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Invitation { + + private final InvitationContent content; + + private final Meta meta; + + private Invitation(final InvitationContent content, final Meta meta) { + this.meta = meta; + this.content = content; + } + + public Invitation(final String email) { + this(new InvitationContent(email), null); + } + + @JsonProperty // because getter is private + private InvitationContent getContent() { + return content; + } + + public Meta getMeta() { + return meta; + } + + @JsonIgnore + public String getEmail() { + return content != null ? content.getEmail() : null; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class InvitationContent { + + private final String email; + + private InvitationContent(final String email) { + this.email = notEmpty(email, "email"); + } + + public String getEmail() { + return email; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + } +} \ No newline at end of file diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Invitations.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Invitations.java new file mode 100644 index 000000000..d53621f37 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Invitations.java @@ -0,0 +1,40 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +import static java.util.Arrays.asList; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Invitations { + + /** + * @see Project#getInvitationsUri() + */ + public static final String URI = Project.URI + "/invitations"; + + private final List invitations; + + public Invitations(Invitation... invitations) { + this.invitations = asList(invitations); + } + + public List getInvitations() { + return invitations; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Project.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Project.java new file mode 100644 index 000000000..65e870ef0 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Project.java @@ -0,0 +1,537 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.GDZonedDateTimeDeserializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Meta; +import com.gooddata.sdk.model.util.UriHelper; + +import java.time.ZonedDateTime; +import java.util.HashSet; +import java.util.Set; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; +import static com.gooddata.sdk.common.util.Validate.notNull; +import static com.gooddata.sdk.common.util.Validate.notNullState; +import static java.util.Arrays.asList; + +/** + * Project in GoodData platform + */ +@JsonTypeName("project") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Project { + + public static final String URI = Projects.URI + "/{id}"; + private static final Set PREPARING_STATES = new HashSet<>(asList("PREPARING", "PREPARED", "LOADING")); + + @JsonProperty("content") + private final ProjectContent content; + + @JsonProperty("meta") + private final ProjectMeta meta; + + @JsonIgnore + private Links links; + + public Project(String title, String authorizationToken) { + content = new ProjectContent(authorizationToken); + meta = new ProjectMeta(title); + } + + public Project(String title, String summary, String authorizationToken) { + content = new ProjectContent(authorizationToken); + meta = new ProjectMeta(title, summary); + } + + @JsonCreator + private Project(@JsonProperty("content") ProjectContent content, @JsonProperty("meta") ProjectMeta meta, + @JsonProperty("links") Links links) { + this.content = content; + this.meta = meta; + this.links = links; + } + + public void setProjectTemplate(String uri) { + meta.setProjectTemplate(uri); + } + + @JsonIgnore + public String getId() { + return UriHelper.getLastUriPart(getUri()); + } + + @JsonIgnore + public String getState() { + return content.getState(); + } + + @JsonIgnore + public String getAuthorizationToken() { + return content.getAuthorizationToken(); + } + + @JsonIgnore + public String getDriver() { + return content.getDriver(); + } + + public void setDriver(String driver) { + notEmpty(driver, "driver"); + content.setDriver(driver); + } + + public void setDriver(ProjectDriver driver) { + notNull(driver, "driver"); + setDriver(driver.getValue()); + } + + @JsonIgnore + public String getGuidedNavigation() { + return content.getGuidedNavigation(); + } + + @JsonIgnore + public String getCluster() { + return content.getCluster(); + } + + @JsonIgnore + public Boolean isPublic() { + return "1".equals(content.getIsPublic()); + } + + @JsonIgnore + public String getTitle() { + return meta.getTitle(); + } + + @JsonIgnore + public String getSummary() { + return meta.getSummary(); + } + + @JsonIgnore + public String getAuthor() { + return meta.getAuthor(); + } + + @JsonIgnore + public String getContributor() { + return meta.getContributor(); + } + + @JsonIgnore + public ZonedDateTime getCreated() { + return meta.getCreated(); + } + + @JsonIgnore + public ZonedDateTime getUpdated() { + return meta.getUpdated(); + } + + @JsonIgnore + public String getUri() { + return notNullState(links, "links").getSelf(); + } + + @JsonIgnore + public String getUsersUri() { + return notNullState(links, "links").getUsers(); + } + + @JsonIgnore + public String getRolesUri() { + return notNullState(links, "links").getRoles(); + } + + @JsonIgnore + public String getGroupsUri() { + return notNullState(links, "links").getGroups(); + } + + @JsonIgnore + public String getInvitationsUri() { + return notNullState(links, "links").getInvitations(); + } + + @JsonIgnore + public String getLdmUri() { + return links.getLdm(); + } + + @JsonIgnore + public String getLdmThumbnailUri() { + return notNullState(links, "links").getLdmThumbnail(); + } + + @JsonIgnore + public String getMetadataUri() { + return notNullState(links, "links").getMetadata(); + } + + @JsonIgnore + public String getPublicArtifactsUri() { + return notNullState(links, "links").getPublicArtifacts(); + } + + @JsonIgnore + public String getTemplatesUri() { + return notNullState(links, "links").getTemplates(); + } + + @JsonIgnore + public String getConnectorsUri() { + return notNullState(links, "links").getConnectors(); + } + + @JsonIgnore + public String getDataLoadUri() { + return notNullState(links, "links").getDataLoad(); + } + + @JsonIgnore + public String getSchedulesUri() { + return notNullState(links, "links").getSchedules(); + } + + @JsonIgnore + public String getExecuteUri() { + return notNullState(links, "links").getExecute(); + } + + /** + * @deprecated This service has been removed from platform long time ago. + */ + @JsonIgnore + @Deprecated + public String getEventStoresUri() { + return notNullState(links, "links").getEventStores(); + } + + @JsonIgnore + public String getClearCachesUri() { + return notNullState(links, "links").getClearCaches(); + } + + @JsonIgnore + public String getUploadsUri() { + return notNullState(links, "links").getUploads(); + } + + @JsonIgnore + public boolean isPreparing() { + return PREPARING_STATES.contains(getState()); + } + + @JsonIgnore + public boolean isEnabled() { + return "ENABLED".equals(getState()); + } + + @JsonIgnore + public String getEnvironment() { + return content.getEnvironment(); + } + + @JsonIgnore + public void setEnvironment(final String environment) { + content.setEnvironment(environment); + } + + public void setEnvironment(final ProjectEnvironment environment) { + notNull(environment, "environment"); + setEnvironment(environment.name()); + } + + public void setEnvironment(final Environment environment) { + notNull(environment, "environment"); + setEnvironment(environment.name()); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class ProjectContent { + + @JsonProperty("authorizationToken") + private final String authorizationToken; + @JsonProperty("guidedNavigation") + private final String guidedNavigation; + @JsonProperty("driver") + private String driver; + @JsonIgnore + private String cluster; + + @JsonIgnore + private String isPublic; + + @JsonIgnore + private String state; + + @JsonProperty + private String environment; + + public ProjectContent(final String authorizationToken) { + this.authorizationToken = authorizationToken; + guidedNavigation = "1"; + driver = ProjectDriver.POSTGRES.getValue(); + } + + @JsonCreator + public ProjectContent(@JsonProperty("authorizationToken") String authorizationToken, + @JsonProperty("driver") String driver, + @JsonProperty("cluster") String cluster, + @JsonProperty("guidedNavigation") String guidedNavigation, + @JsonProperty("isPublic") String isPublic, + @JsonProperty("environment") String environment, + @JsonProperty("state") String state) { + this.authorizationToken = authorizationToken; + this.guidedNavigation = guidedNavigation; + this.driver = driver; + this.cluster = cluster; + this.isPublic = isPublic; + this.state = state; + this.environment = environment; + } + + public String getState() { + return state; + } + + public String getAuthorizationToken() { + return authorizationToken; + } + + public String getDriver() { + return driver; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + public String getGuidedNavigation() { + return guidedNavigation; + } + + public String getCluster() { + return cluster; + } + + public String getIsPublic() { + return isPublic; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(final String environment) { + this.environment = environment; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Links { + private final String self; + private final String users; + private final String roles; + private final String groups; + private final String invitations; + private final String ldm; + private final String ldmThumbnail; + private final String metadata; + private final String publicArtifacts; + private final String templates; + private final String connectors; + private final String dataLoad; + private final String schedules; + private final String execute; + private final String eventStores; + private final String clearCaches; + private final String uploads; + + @JsonCreator + public Links(@JsonProperty("self") String self, + @JsonProperty("users") String users, + @JsonProperty("roles") String roles, + @JsonProperty("groups") String groups, + @JsonProperty("invitations") String invitations, + @JsonProperty("ldm") String ldm, + @JsonProperty("ldm_thumbnail") String ldmThumbnail, + @JsonProperty("metadata") String metadata, + @JsonProperty("publicartifacts") String publicArtifacts, + @JsonProperty("templates") String templates, + @JsonProperty("connectors") String connectors, + @JsonProperty("dataload") String dataLoad, + @JsonProperty("schedules") String schedules, + @JsonProperty("execute") String execute, + @JsonProperty("eventstores") String eventStores, + @JsonProperty("clearCaches") String clearCaches, + @JsonProperty("uploads") String uploads) { + this.self = self; + this.users = users; + this.roles = roles; + this.groups = groups; + this.invitations = invitations; + this.ldm = ldm; + this.ldmThumbnail = ldmThumbnail; + this.metadata = metadata; + this.publicArtifacts = publicArtifacts; + this.templates = templates; + this.connectors = connectors; + this.dataLoad = dataLoad; + this.schedules = schedules; + this.execute = execute; + this.eventStores = eventStores; + this.clearCaches = clearCaches; + this.uploads = uploads; + } + + public String getSelf() { + return self; + } + + public String getUsers() { + return users; + } + + public String getRoles() { + return roles; + } + + public String getGroups() { + return groups; + } + + public String getInvitations() { + return invitations; + } + + public String getLdm() { + return ldm; + } + + public String getLdmThumbnail() { + return ldmThumbnail; + } + + public String getMetadata() { + return metadata; + } + + public String getPublicArtifacts() { + return publicArtifacts; + } + + public String getTemplates() { + return templates; + } + + public String getConnectors() { + return connectors; + } + + public String getDataLoad() { + return dataLoad; + } + + public String getSchedules() { + return schedules; + } + + public String getExecute() { + return execute; + } + + /** + * @deprecated This service has been removed from platform long time ago. + */ + @Deprecated + public String getEventStores() { + return eventStores; + } + + public String getClearCaches() { + return clearCaches; + } + + public String getUploads() { + return uploads; + } + } + + private static class ProjectMeta extends Meta { + + private String projectTemplate; + + @JsonCreator + private ProjectMeta(@JsonProperty("author") String author, + @JsonProperty("contributor") String contributor, + @JsonProperty("created") @JsonDeserialize(using = GDZonedDateTimeDeserializer.class) ZonedDateTime created, + @JsonProperty("updated") @JsonDeserialize(using = GDZonedDateTimeDeserializer.class) ZonedDateTime updated, + @JsonProperty("summary") String summary, + @JsonProperty("title") String title, + @JsonProperty("category") String category, + @JsonProperty("tags") Set tags, + @JsonProperty("uri") String uri, + @JsonProperty("identifier") String identifier, + @JsonProperty("deprecated") @JsonDeserialize(using = BooleanDeserializer.class) Boolean deprecated, + @JsonProperty("isProduction") @JsonDeserialize(using = BooleanDeserializer.class) Boolean production, + @JsonProperty("locked") @JsonDeserialize(using = BooleanDeserializer.class) Boolean locked, + @JsonProperty("unlisted") @JsonDeserialize(using = BooleanDeserializer.class) Boolean unlisted, + @JsonProperty("sharedWithSomeone") @JsonDeserialize(using = BooleanDeserializer.class) Boolean sharedWithSomeone, + @JsonProperty("flags") Set flags) { + super(author, contributor, created, updated, summary, title, category, tags, uri, identifier, + deprecated, production, locked, unlisted, sharedWithSomeone, flags); + + } + + private ProjectMeta(String title) { + super(title); + } + + private ProjectMeta(String title, String summary) { + super(title, summary); + } + + public String getProjectTemplate() { + return projectTemplate; + } + + public void setProjectTemplate(String projectTemplate) { + this.projectTemplate = projectTemplate; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectDriver.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectDriver.java new file mode 100644 index 000000000..cea08b357 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectDriver.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import static com.gooddata.sdk.common.util.Validate.notEmpty; + +public enum ProjectDriver { + POSTGRES("Pg"), + VERTICA("vertica"); + + private final String value; + + ProjectDriver(String value) { + notEmpty(value, "value"); + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectEnvironment.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectEnvironment.java new file mode 100644 index 000000000..f12a4ec4c --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectEnvironment.java @@ -0,0 +1,26 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +/** + * Optional property for project create, the property is ignored during project update. + * Default value is {@link #PRODUCTION} which is also environment for all currently existing projects.

+ * Preffer {@link Environment} if possible. + */ +public enum ProjectEnvironment { + /** + * Default value, projects are backed-up and archived. + */ + PRODUCTION, + /** + * no meaning yet and behavior is the same as for {@link #PRODUCTION} projects + */ + DEVELOPMENT, + /** + * 'TESTING' projects are not backed-up and archived. + */ + TESTING +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectTemplate.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectTemplate.java new file mode 100644 index 000000000..c1b877d09 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectTemplate.java @@ -0,0 +1,52 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Project template. + * Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ProjectTemplate { + + public static final String URI = "/gdc/md/{projectId}/templates"; + + private final String url; + private final String urn; + private final String version; + + @JsonCreator + public ProjectTemplate(@JsonProperty("url") String url, @JsonProperty("urn") String urn, + @JsonProperty("version") String version) { + this.url = url; + this.urn = urn; + this.version = version; + } + + public String getUrl() { + return url; + } + + public String getUrn() { + return urn; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectTemplates.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectTemplates.java new file mode 100644 index 000000000..5a85c1932 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectTemplates.java @@ -0,0 +1,39 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; + +/** + * Collection of project templates. + * Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ProjectTemplates { + + private final Collection templatesInfo; + + @JsonCreator + ProjectTemplates(@JsonProperty("templatesInfo") Collection templatesInfo) { + this.templatesInfo = templatesInfo; + } + + public Collection getTemplatesInfo() { + return templatesInfo; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectUsersUpdateResult.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectUsersUpdateResult.java new file mode 100644 index 000000000..a3ff57711 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectUsersUpdateResult.java @@ -0,0 +1,51 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import java.util.List; + +/** + * Result of project users update + */ +@JsonTypeName("projectUsersUpdateResult") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ProjectUsersUpdateResult { + + private final List successful; + private final List failed; + + @JsonCreator + private ProjectUsersUpdateResult(@JsonProperty("successful") final List successful, + @JsonProperty("failed") final List failed) { + this.successful = successful; + this.failed = failed; + } + + /** + * + * @return List of account URIs successfully updated + */ + public List getSuccessful() { + return successful; + } + + /** + * + * @return List of account URIs failed to update + */ + public List getFailed() { + return failed; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResult.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResult.java new file mode 100644 index 000000000..a7bb18a28 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResult.java @@ -0,0 +1,101 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +/** + * Represents one validation result. + * Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProjectValidationResult { + + private static final String LEVEL_WARNING = "WARN"; + private static final String LEVEL_ERROR = "ERROR"; + private final String category; + private final String level; + private final String message; + private final List params; + private ProjectValidationType validation; + + @JsonCreator + private ProjectValidationResult(@JsonProperty("ecat") String category, @JsonProperty("level") String level, + @JsonProperty("msg") String message, @JsonProperty("pars") List params) { + this.category = category; + this.level = level; + this.message = message; + this.params = params; + } + + public String getCategory() { + return category; + } + + public String getLevel() { + return level; + } + + public String getMessage() { + return message; + } + + public List getParams() { + return params; + } + + public boolean isError() { + return LEVEL_ERROR.equals(level); + } + + public ProjectValidationType getValidation() { + return validation; + } + + // package protected by design (used inside ProjectValidationResults deserialization constructor) + void setValidation(ProjectValidationType validation) { + this.validation = validation; + } + + public boolean isWarning() { + return LEVEL_WARNING.equals(level); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ProjectValidationResult that = (ProjectValidationResult) o; + + if (category != null ? !category.equals(that.category) : that.category != null) return false; + if (level != null ? !level.equals(that.level) : that.level != null) return false; + if (message != null ? !message.equals(that.message) : that.message != null) return false; + if (params != null ? !params.equals(that.params) : that.params != null) return false; + return validation != null ? validation.equals(that.validation) : that.validation == null; + } + + @Override + public int hashCode() { + int result = category != null ? category.hashCode() : 0; + result = 31 * result + (level != null ? level.hashCode() : 0); + result = 31 * result + (message != null ? message.hashCode() : 0); + result = 31 * result + (params != null ? params.hashCode() : 0); + result = 31 * result + (validation != null ? validation.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultGdcTimeElParam.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultGdcTimeElParam.java new file mode 100644 index 000000000..a9e8056ee --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultGdcTimeElParam.java @@ -0,0 +1,54 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +@JsonTypeName("gdctime_el") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProjectValidationResultGdcTimeElParam extends ProjectValidationResultParam { + + private final List ids; + + ProjectValidationResultGdcTimeElParam(final List ids) { + this.ids = ids; + } + + @JsonCreator + private static ProjectValidationResultGdcTimeElParam create(@JsonProperty("ids") List ids) { + return new ProjectValidationResultGdcTimeElParam(ids); + } + + public List getIds() { + return ids; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ProjectValidationResultGdcTimeElParam that = (ProjectValidationResultGdcTimeElParam) o; + + return ids != null ? ids.equals(that.ids) : that.ids == null; + } + + @Override + public int hashCode() { + return ids != null ? ids.hashCode() : 0; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultItem.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultItem.java new file mode 100644 index 000000000..82410caa6 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultItem.java @@ -0,0 +1,59 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.List; + +/** + * Validation result body helper dto. Not exposed to user. + * Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +class ProjectValidationResultItem { + + private final Body body; + private final ProjectValidationType validation; + + @JsonCreator + private ProjectValidationResultItem(@JsonProperty("body") Body body, @JsonProperty("from") ProjectValidationType validation) { + this.body = body; + this.validation = validation; + } + + List getLogs() { + return body.logs; + } + + ProjectValidationType getValidation() { + return validation; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Body { + + private final List logs; + + @JsonCreator + private Body(@JsonProperty("log") List logs) { + this.logs = logs; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultObjectParam.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultObjectParam.java new file mode 100644 index 000000000..aca9cb899 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultObjectParam.java @@ -0,0 +1,60 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +@JsonTypeName("object") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProjectValidationResultObjectParam extends ProjectValidationResultParam { + private final String name; + private final String uri; + + ProjectValidationResultObjectParam(String name, String uri) { + this.name = name; + this.uri = uri; + } + + @JsonCreator + private static ProjectValidationResultObjectParam create(@JsonProperty("name") String name, @JsonProperty("uri") String uri) { + return new ProjectValidationResultObjectParam(name, uri); + } + + public String getName() { + return name; + } + + public String getUri() { + return uri; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ProjectValidationResultObjectParam that = (ProjectValidationResultObjectParam) o; + + if (name != null ? !name.equals(that.name) : that.name != null) return false; + return uri != null ? uri.equals(that.uri) : that.uri == null; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (uri != null ? uri.hashCode() : 0); + return result; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultParam.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultParam.java new file mode 100644 index 000000000..cd8dc6a38 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultParam.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Represents validation result message param, must be of certain type. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSubTypes({ + @JsonSubTypes.Type(name = "common", value = ProjectValidationResultStringParam.class), + @JsonSubTypes.Type(name = "object", value = ProjectValidationResultObjectParam.class), + @JsonSubTypes.Type(name = "sli_el", value = ProjectValidationResultSliElParam.class), + @JsonSubTypes.Type(name = "gdctime_el", value = ProjectValidationResultGdcTimeElParam.class), +}) +public abstract class ProjectValidationResultParam { +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultSliElParam.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultSliElParam.java new file mode 100644 index 000000000..9849fa8fc --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultSliElParam.java @@ -0,0 +1,111 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.String.format; + +@JsonTypeName("sli_el") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProjectValidationResultSliElParam extends ProjectValidationResultParam { + + private final List ids; + private final List vals; + + ProjectValidationResultSliElParam(final List ids, final List vals) { + this.ids = ids; + this.vals = vals; + } + + @JsonCreator + private static ProjectValidationResultSliElParam create(@JsonProperty("ids") List ids, @JsonProperty("vals") List vals) { + return new ProjectValidationResultSliElParam(ids, vals); + } + + /** + * @return list of IDs. IDs are primary property of this param. + */ + public List getIds() { + return ids; + } + + /** + * @return list of values. Values have only informative character and are connected to IDs. + * @see #getIds() + */ + public List getVals() { + return vals; + } + + /** + * Returns map of tuples {@code }. Tuples are constructed from lists of IDs and values + * according these assumptions: + *

    + *
  • Sizes of both lists are equal.
  • + *
  • ID and it's corresponding value are on the same index.
  • + *
+ * + * @return {@code null} when ids are {@code null} or map of tuples + * @throws IllegalStateException
    + *
  • when ids are not {@code null} and vals are {@code null}
  • + *
  • when sizes of ids and vals lists are not equal
  • + *
+ * @see #getIds() + * @see #getVals() + */ + public Map asMap() { + if (ids == null) { + return null; + } + + if (vals == null) { + throw new IllegalStateException("Values not defined for IDs."); + } + + if (ids.size() != vals.size()) { + throw new IllegalStateException(format("Size of list of IDs (%s) and corresponding list of their values (%s)" + + " is not equal.", ids.size(), vals.size())); + } + + final Map sliElParamMap = new LinkedHashMap<>(ids.size()); + for (int index = 0; index < ids.size(); index++) { + sliElParamMap.put(ids.get(index), vals.get(index)); + } + return sliElParamMap; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ProjectValidationResultSliElParam that = (ProjectValidationResultSliElParam) o; + + if (ids != null ? !ids.equals(that.ids) : that.ids != null) return false; + return vals != null ? vals.equals(that.vals) : that.vals == null; + } + + @Override + public int hashCode() { + int result = ids != null ? ids.hashCode() : 0; + result = 31 * result + (vals != null ? vals.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultStringParam.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultStringParam.java new file mode 100644 index 000000000..7be9c85cb --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResultStringParam.java @@ -0,0 +1,51 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +@JsonTypeName("common") +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProjectValidationResultStringParam extends ProjectValidationResultParam { + private final String value; + + ProjectValidationResultStringParam(String value) { + this.value = value; + } + + // TODO is there some BUG in jackson preventing use the contructor as creator? + @JsonCreator + private static ProjectValidationResultStringParam create(String value) { + return new ProjectValidationResultStringParam(value); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ProjectValidationResultStringParam that = (ProjectValidationResultStringParam) o; + + return value != null ? value.equals(that.value) : that.value == null; + } + + @Override + public int hashCode() { + return value != null ? value.hashCode() : 0; + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResults.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResults.java new file mode 100644 index 000000000..ce8515b56 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationResults.java @@ -0,0 +1,108 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.LinkedList; +import java.util.List; + +/** + * Results of project validation. + *

+ * Deserialization only. + */ +@JsonTypeName("projectValidateResult") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ProjectValidationResults { + + private final boolean error; + private final boolean fatalError; + private final boolean warning; + + private final List results = new LinkedList<>(); + + @JsonCreator + private ProjectValidationResults(@JsonProperty("error_found") @JsonDeserialize(using = BooleanDeserializer.class) boolean error, + @JsonProperty("fatal_error_found") @JsonDeserialize(using = BooleanDeserializer.class) boolean fatalError, + @JsonProperty("results") List resultItems) { + this.error = error; + this.fatalError = fatalError; + + boolean hasWarning = false; + for (ProjectValidationResultItem resultItem : resultItems) { + final List itemResults = resultItem.getLogs(); + if (itemResults != null) { + for (ProjectValidationResult log : itemResults) { + log.setValidation(resultItem.getValidation()); + this.results.add(log); + hasWarning = hasWarning || log.isWarning(); + } + } + } + + this.warning = hasWarning; + } + + /** + * True if validation found some error + * + * @return true in case of validation error, false otherwise + */ + public boolean isError() { + return error; + } + + /** + * True in case some part of validation crashed (not executed at all) + * + * @return true if validation crashed, false otherwise + */ + public boolean isFatalError() { + return fatalError; + } + + /** + * True if some warning was found during validation. + * + * @return true if some warning was found during validation, false otherwise + */ + public boolean isWarning() { + return warning; + } + + /** + * True if no warning or error is in the validation results. + * + * @return true if no warning or error is in the validation results, false otherwise + */ + public boolean isValid() { + return !(error || fatalError || warning); + } + + /** + * Get validation results, describing output of all validations executed. + * Can return empty list, in case the validation had no error or warning (is valid)- + * + * @return validation results list + */ + public List getResults() { + return results; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationType.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationType.java new file mode 100644 index 000000000..45c2e94aa --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidationType.java @@ -0,0 +1,55 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +/** + * Represents project validation type. + */ +public class ProjectValidationType { + + public static final ProjectValidationType PDM_VS_DWH = new ProjectValidationType("pdm::pdm_vs_dwh"); + public static final ProjectValidationType METRIC_FILTER = new ProjectValidationType("metric_filter"); + public static final ProjectValidationType PDM_TRANSITIVITY = new ProjectValidationType("pdm::transitivity"); + public static final ProjectValidationType LDM = new ProjectValidationType("ldm"); + public static final ProjectValidationType INVALID_OBJECTS = new ProjectValidationType("invalid_objects"); + public static final ProjectValidationType PDM_ELEM = new ProjectValidationType("pdm::elem_validation"); + public static final ProjectValidationType PDM_PK_FK_CONSISTENCY = new ProjectValidationType("pdm::pk_fk_consistency"); + private final String value; + + @JsonCreator + public ProjectValidationType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ProjectValidationType that = (ProjectValidationType) o; + + return value != null ? value.equals(that.value) : that.value == null; + } + + @Override + public int hashCode() { + return value != null ? value.hashCode() : 0; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidations.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidations.java new file mode 100644 index 000000000..e9c2e5ecc --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/ProjectValidations.java @@ -0,0 +1,50 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.HashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +/** + * Possible validations for project. + * Helper dto, to fetch available validations or start validations. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ProjectValidations { + + public static final String URI = "/gdc/md/{projectId}/validate"; + + private final Set validations; + + ProjectValidations(ProjectValidationType... validations) { + this(new HashSet<>(asList(validations))); + } + + @JsonCreator + public ProjectValidations(@JsonProperty("availableValidations") final Set validations) { + this.validations = validations; + } + + @JsonProperty("validateProject") + public Set getValidations() { + return validations; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Projects.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Projects.java new file mode 100644 index 000000000..f0dec6442 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Projects.java @@ -0,0 +1,70 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.collections.Page; +import com.gooddata.sdk.common.collections.PageDeserializer; +import com.gooddata.sdk.common.collections.Paging; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.gooddata.sdk.model.project.Projects.ROOT_NODE; + +/** + * Collection of GoodData projects being returned from API. + */ +@JsonDeserialize(using = Projects.Deserializer.class) +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName(ROOT_NODE) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Projects extends Page { + + public static final String URI = "/gdc/projects"; + public static final String LIST_PROJECTS_URI = "/gdc/account/profile/{id}/projects"; + + static final String ROOT_NODE = "projects"; + + public Projects(List items, Paging paging) { + super(items, paging); + } + + /** + * @return the list of current page items only + * @deprecated use {@link Page#getPageItems()} or other methods from {@link Page}. + * Deprecated since version 3.0.0. Will be removed in one of future versions. + */ + @Deprecated + public Collection getProjects() { + return getPageItems(); + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + static class Deserializer extends PageDeserializer { + + protected Deserializer() { + super(Project.class); + } + + @Override + protected Projects createPage(final List items, final Paging paging, final Map links) { + return new Projects(items, paging); + } + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Role.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Role.java new file mode 100644 index 000000000..b10e3639a --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Role.java @@ -0,0 +1,131 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; +import com.gooddata.sdk.common.util.BooleanStringSerializer; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.md.Meta; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Project Role + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("projectRole") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Role { + public static final String URI = "/gdc/projects/{projectId}/roles/{roleId}"; + + private static final String SELF_LINK = "self"; + + @JsonDeserialize(contentUsing = BooleanDeserializer.class) + @JsonSerialize(contentUsing = BooleanStringSerializer.class) + private final Map permissions; + + private final Meta meta; + + private final Map links; + + @JsonCreator + Role(@JsonProperty("permissions") final Map permissions, + @JsonProperty("meta") final Meta meta, + @JsonProperty("links") final Map links) { + this.permissions = permissions == null ? new HashMap<>() : permissions; + this.meta = meta == null ? new Meta(null) : meta; + this.links = links == null ? new HashMap<>() : links; + } + + /** + * Returns set of permission names this role can have granted. + * + * @return set of permission names + */ + public Set getPermissions() { + return Collections.unmodifiableSet(permissions.keySet()); + } + + /** + * Returns names of granted permissions. + * + * @return set of granted permissions + */ + public Set getGrantedPermissions() { + return permissions.entrySet().stream().filter(Entry::getValue).map(Entry::getKey).collect(Collectors.toSet()); + } + + /** + * Returns true if provided permission is granted. + * + * @param permission permission name to test + * @return whether the permission is granted + */ + public boolean hasPermissionGranted(final String permission) { + return permissions.get(permission); + } + + public String getTitle() { + return meta.getTitle(); + } + + public String getIdentifier() { + return meta.getIdentifier(); + } + + @JsonIgnore + public String getUri() { + return links.get(SELF_LINK); + } + + /** + * Allows service to set self link as it is not provided by REST API. + *

+ * NOTE: This is intentionally left package-private. + */ + public void setUri(final String uri) { + links.put(SELF_LINK, uri); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Role role = (Role) o; + + if (!permissions.equals(role.permissions)) return false; + return !(getIdentifier() != null ? !getIdentifier().equals(role.getIdentifier()) : role.getIdentifier() != null); + + } + + @Override + public int hashCode() { + int result = permissions.hashCode(); + result = 31 * result + (getIdentifier() != null ? getIdentifier().hashCode() : 0); + return result; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Roles.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Roles.java new file mode 100644 index 000000000..03b4aeda4 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Roles.java @@ -0,0 +1,41 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import java.util.Set; + +/** + * List of Role URIs. Deserialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("projectRoles") +@JsonIgnoreProperties(ignoreUnknown = true) +public class Roles { + public static final String URI = "/gdc/projects/{projectId}/roles"; + + private final Set roles; + + @JsonCreator + Roles(@JsonProperty("roles") final Set roles) { + this.roles = roles; + } + + public Set getRoles() { + return roles; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/User.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/User.java new file mode 100644 index 000000000..d94da7aa0 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/User.java @@ -0,0 +1,198 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; +import com.gooddata.sdk.model.account.Account; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * User in project + * + * @see Account + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("user") +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class User { + + public static final String URI = "/gdc/projects/{projectId}/users/{userId}"; + + @JsonProperty + private UserContent content; + + private Links links; + + @JsonCreator + User(@JsonProperty("content") final UserContent content, + @JsonProperty("links") final Links links) { + this.content = content; + this.links = links; + } + + public User(final Account account, + final Role... userRoles) { + final List userRoleUris = Arrays.asList(userRoles).stream().map(e -> e.getUri()).collect(Collectors.toList()); + + links = new Links(account.getUri()); + content = new UserContent("ENABLED", userRoleUris); + } + + @JsonIgnore + public String getEmail() { + return content.getEmail(); + } + + @JsonIgnore + public String getStatus() { + return content.getStatus(); + } + + public void setStatus(final String status) { + this.content.status = status; + } + + @JsonIgnore + public String getLastName() { + return content.getLastName(); + } + + @JsonIgnore + public List getUserRoles() { + return content.getUserRoles(); + } + + @JsonIgnore + public String getLogin() { + return content.getLogin(); + } + + @JsonIgnore + public String getFirstName() { + return content.getFirstName(); + } + + @JsonIgnore + public String getPhoneNumber() { + return content.getPhoneNumber(); + } + + public Links getLinks() { + return links; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class UserContent { + + @JsonProperty("email") + private String email; + + @JsonProperty("firstname") + private String firstName; + + @JsonProperty("userRoles") + private List userRoles; + + @JsonProperty("phonenumber") + private String phoneNumber; + + @JsonProperty("status") + private String status; + + @JsonProperty("lastname") + private String lastName; + + @JsonProperty("login") + private String login; + + @JsonCreator + public UserContent(@JsonProperty("email") final String email, + @JsonProperty("firstname") final String firstName, + @JsonProperty("userRoles") final List userRoles, + @JsonProperty("phonenumber") final String phoneNumber, + @JsonProperty("status") final String status, + @JsonProperty("lastname") final String lastName, + @JsonProperty("login") final String login) { + this.email = email; + this.firstName = firstName; + this.userRoles = userRoles; + this.phoneNumber = phoneNumber; + this.status = status; + this.lastName = lastName; + this.login = login; + } + + private UserContent(final String status, final List userRoles) { + this.userRoles = userRoles; + this.status = status; + } + + public String getEmail() { + return email; + } + + public String getFirstName() { + return firstName; + } + + public List getUserRoles() { + return userRoles; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public String getStatus() { + return status; + } + + public String getLastName() { + return lastName; + } + + public String getLogin() { + return login; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class Links { + + private String self; + + private Links(@JsonProperty("self") final String self) { + this.self = self; + } + + public String getSelf() { + return self; + } + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Users.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Users.java new file mode 100644 index 000000000..43c33ab88 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/Users.java @@ -0,0 +1,42 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.gooddata.sdk.common.collections.Page; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; + +import static com.gooddata.sdk.model.project.Users.ROOT_NODE; +import static java.util.Arrays.asList; + +/** + * List of users. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = Id.NONE) +@JsonTypeName(ROOT_NODE) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonDeserialize(using = UsersDeserializer.class) +@JsonSerialize(using = UsersSerializer.class) +public class Users extends Page { + public static final String URI = "/gdc/projects/{projectId}/users"; + + static final String ROOT_NODE = "users"; + + Users(final List items, final Paging paging) { + super(items, paging); + } + + public Users(final User... users) { + this(asList(users), null); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/UsersDeserializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/UsersDeserializer.java new file mode 100644 index 000000000..a30318c9d --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/UsersDeserializer.java @@ -0,0 +1,24 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + +import com.gooddata.sdk.common.collections.PageDeserializer; +import com.gooddata.sdk.common.collections.Paging; + +import java.util.List; +import java.util.Map; + +class UsersDeserializer extends PageDeserializer { + + protected UsersDeserializer() { + super(User.class, Users.ROOT_NODE); + } + + @Override + protected Users createPage(final List items, final Paging paging, final Map links) { + return new Users(items, paging); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/UsersSerializer.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/UsersSerializer.java new file mode 100644 index 000000000..5eb007cf8 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/UsersSerializer.java @@ -0,0 +1,35 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project; + + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +/** + * Serializer of {@link Users} + */ +class UsersSerializer extends JsonSerializer { + + @Override + public void serialize(final Users users, final JsonGenerator jgen, final SerializerProvider serializerProvider) throws IOException { + jgen.writeStartObject(); + jgen.writeFieldName(Users.ROOT_NODE); + + jgen.writeStartArray(); + final ObjectCodec codec = jgen.getCodec(); + for (Object item : users.getPageItems()) { + codec.writeValue(jgen, item); + } + jgen.writeEndArray(); + + jgen.writeEndObject(); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/DiffRequest.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/DiffRequest.java new file mode 100644 index 000000000..230ada541 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/DiffRequest.java @@ -0,0 +1,54 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRawValue; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; + +import static com.gooddata.sdk.common.util.Validate.notNull; + +/** + * A request to perform diff between current project model and given targetModel. + * Serialization only. + */ +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonTypeName("diffRequest") +@JsonInclude(JsonInclude.Include.ALWAYS) +@JsonIgnoreProperties(ignoreUnknown = true) +public class DiffRequest { + + public static final String URI = "/gdc/projects/{project-id}/model/diff"; + + @JsonRawValue + @JsonProperty("targetModel") + private final String targetModel; + + /** + * @param targetModel desired target state of project model + */ + public DiffRequest(String targetModel) { + this.targetModel = notNull(targetModel, "targetModel"); + } + + /** + * Returns desired target state of project model + * + * @return desired target state of project model + */ + public String getTargetModel() { + return targetModel; + } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/MaqlDdl.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/MaqlDdl.java new file mode 100644 index 000000000..dcd4a6342 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/MaqlDdl.java @@ -0,0 +1,22 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project.model; + +import com.gooddata.sdk.model.gdc.AbstractMaql; + +/** + * MAQL DDL statement. + * Serialization only. + */ +public class MaqlDdl extends AbstractMaql { + + public static final String URI = "/gdc/md/{project}/ldm/manage2"; + + public MaqlDdl(final String maql) { + super(maql); + } + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/MaqlDdlLinks.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/MaqlDdlLinks.java new file mode 100644 index 000000000..4cd3c7caf --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/MaqlDdlLinks.java @@ -0,0 +1,40 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.project.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.gooddata.sdk.model.gdc.LinkEntries; + +import java.util.List; + +/** + * MAQL DDL links (result from POSTing to /ldm/manage2). + * Deserialization only. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public +class MaqlDdlLinks extends LinkEntries { + + private static final String TASKS_STATUS = "tasks-status"; + + @JsonCreator + private MaqlDdlLinks(@JsonProperty("entries") List entries) { + super(entries); + } + + public String getStatusUri() { + for (LinkEntry linkEntry : getEntries()) { + if (TASKS_STATUS.equals(linkEntry.getCategory())) { + return linkEntry.getUri(); + } + } + return null; + } +} diff --git a/src/main/java/com/gooddata/model/ModelDiff.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/ModelDiff.java similarity index 82% rename from src/main/java/com/gooddata/model/ModelDiff.java rename to gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/ModelDiff.java index da41c2042..9f8d2601f 100644 --- a/src/main/java/com/gooddata/model/ModelDiff.java +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/project/model/ModelDiff.java @@ -1,13 +1,16 @@ /* - * Copyright (C) 2007-2014, GoodData(R) Corporation. All rights reserved. + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. */ -package com.gooddata.model; +package com.gooddata.sdk.model.project.model; -import org.codehaus.jackson.annotate.JsonCreator; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonProperty; -import org.codehaus.jackson.annotate.JsonTypeInfo; -import org.codehaus.jackson.annotate.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.gooddata.sdk.common.util.GoodDataToStringBuilder; import java.util.Collections; import java.util.List; @@ -33,7 +36,7 @@ public class ModelDiff { */ @JsonCreator ModelDiff(@JsonProperty("updateScripts") List updateScripts) { - this.updateScripts = updateScripts == null ? Collections.emptyList() : updateScripts; + this.updateScripts = updateScripts == null ? Collections.emptyList() : updateScripts; } /** @@ -99,24 +102,29 @@ private UpdateScript getUpdateScriptByFlags(final boolean preserveData, final bo return result; } + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } + /** * Set of MAQL DDL scripts with one variant of side-effects (truncation of loaded data, drops of related objects...). */ @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) @JsonTypeName("updateScript") @JsonIgnoreProperties(ignoreUnknown = true) - static class UpdateScript { + public static class UpdateScript { - private List maqlChunks; - private Boolean preserveData; - private Boolean cascadeDrops; + private final List maqlChunks; + private final Boolean preserveData; + private final Boolean cascadeDrops; /** * Create set of MAQL DDL scripts with one variant of side-effects (truncation of loaded data, drops of related objects...). * * @param preserveData true if data should be preserved, false if they should be truncated * @param cascadeDrops true if related objects should be also dropped, false if not - * @param maqlChunks MAQL strings + * @param maqlChunks MAQL strings */ @JsonCreator UpdateScript(@JsonProperty("preserveData") Boolean preserveData, @@ -124,7 +132,7 @@ static class UpdateScript { @JsonProperty("maqlDdlChunks") List maqlChunks) { this.preserveData = preserveData; this.cascadeDrops = cascadeDrops; - this.maqlChunks = maqlChunks == null ? Collections.emptyList() : maqlChunks; + this.maqlChunks = maqlChunks == null ? Collections.emptyList() : maqlChunks; } /** @@ -132,7 +140,7 @@ static class UpdateScript { * * @param preserveData true if data should be preserved, false if they should be truncated * @param cascadeDrops true if related objects should be also dropped, false if not - * @param maqlChunks MAQL strings + * @param maqlChunks MAQL strings */ UpdateScript(boolean preserveData, boolean cascadeDrops, String... maqlChunks) { this(preserveData, cascadeDrops, asList(maqlChunks)); @@ -164,5 +172,10 @@ public Boolean isPreserveData() { public Boolean isCascadeDrops() { return cascadeDrops; } + + @Override + public String toString() { + return GoodDataToStringBuilder.defaultToString(this); + } } } diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/projecttemplate/Template.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/projecttemplate/Template.java new file mode 100644 index 000000000..87ac0b942 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/projecttemplate/Template.java @@ -0,0 +1,194 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.projecttemplate; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gooddata.sdk.common.util.BooleanDeserializer; + +import java.util.List; +import java.util.Objects; + +/** + * Represents one project template. + * Deserialization only. + * + * @deprecated The project templates are deprecated and stopped working on May 15, 2019. + * See https://support.gooddata.com/hc/en-us/articles/360016126334-April-4-2019 + * Deprecated since version 3.0.1. Will be removed in one of future versions. + */ +@Deprecated +@JsonTypeName("projectTemplate") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Template { + + private final String uri; + private final String urn; + private final String version; + private final Boolean hidden; + private final Content content; + private final Meta meta; + + @JsonCreator + private Template(@JsonProperty("link") String uri, @JsonProperty("urn") String urn, @JsonProperty("version") String version, + @JsonProperty("hidden") @JsonDeserialize(using = BooleanDeserializer.class) Boolean hidden, + @JsonProperty("content") Content content, @JsonProperty("meta") Meta meta) { + this.uri = uri; + this.urn = urn; + this.version = version; + this.hidden = hidden; + this.content = content; + this.meta = meta; + } + + public String getUri() { + return uri; + } + + public String getUrn() { + return urn; + } + + public String getVersion() { + return version; + } + + public Boolean getHidden() { + return hidden; + } + + public String getMdDefinitionUri() { + return content != null ? content.getMdDefinitionUri() : null; + } + + public String getDwDefinitionUri() { + return content != null ? content.getDwDefinitionUri() : null; + } + + public String getConfigUri() { + return content != null ? content.getConfigUri() : null; + } + + public List getManifestsUris() { + return content != null ? content.getManifestsUris() : null; + } + + public Meta getMeta() { + return meta; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Template template = (Template) o; + return Objects.equals(uri, template.uri); + } + + @Override + public int hashCode() { + return Objects.hash(uri); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class Content { + private final String mdDefinitionUri; + private final String dwDefinitionUri; + private final String configUri; + private final List manifestsUris; + + @JsonCreator + private Content(@JsonProperty("MDDefinition") String mdDefinitionUri, @JsonProperty("DWDefinition") String dwDefinitionUri, + @JsonProperty("config") String configUri, @JsonProperty("manifests") List manifestsUris) { + this.mdDefinitionUri = mdDefinitionUri; + this.dwDefinitionUri = dwDefinitionUri; + this.configUri = configUri; + this.manifestsUris = manifestsUris; + } + + public String getMdDefinitionUri() { + return mdDefinitionUri; + } + + public String getDwDefinitionUri() { + return dwDefinitionUri; + } + + public String getConfigUri() { + return configUri; + } + + public List getManifestsUris() { + return manifestsUris; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + static class Meta { + private final String summary; + private final String title; + private final String category; + private final String apiVersion; + private final Author author; + + @JsonCreator + private Meta(@JsonProperty("summary") String summary, @JsonProperty("title") String title, @JsonProperty("category") String category, + @JsonProperty("apiVersion") String apiVersion, @JsonProperty("author") Author author) { + this.summary = summary; + this.title = title; + this.category = category; + this.apiVersion = apiVersion; + this.author = author; + } + + public String getSummary() { + return summary; + } + + public String getTitle() { + return title; + } + + public String getCategory() { + return category; + } + + public String getApiVersion() { + return apiVersion; + } + + public Author getAuthor() { + return author; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + static class Author { + private final String name; + private final String uri; + + @JsonCreator + private Author(@JsonProperty("name") String name, @JsonProperty("uri") String uri) { + this.name = name; + this.uri = uri; + } + + public String getName() { + return name; + } + + public String getUri() { + return uri; + } + } + + +} diff --git a/gooddata-java-model/src/main/java/com/gooddata/sdk/model/projecttemplate/Templates.java b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/projecttemplate/Templates.java new file mode 100644 index 000000000..a23116889 --- /dev/null +++ b/gooddata-java-model/src/main/java/com/gooddata/sdk/model/projecttemplate/Templates.java @@ -0,0 +1,37 @@ +/* + * (C) 2025 GoodData Corporation. + * This source code is licensed under the BSD-style license found in the + * LICENSE.txt file in the root directory of this source tree. + */ +package com.gooddata.sdk.model.projecttemplate; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * Wrapper for list of project templates. + * Deserialization only. + * + * @deprecated The project templates are deprecated and stopped working on May 15, 2019. + * See https://support.gooddata.com/hc/en-us/articles/360016126334-April-4-2019 + * Deprecated since version 3.0.1. Will be removed in one of future versions. + */ +@Deprecated +@JsonIgnoreProperties(ignoreUnknown = true) +public class Templates { + + public static final String URI = "/projectTemplates"; + private final List