├── .gitignore ├── LICENSE ├── README.md ├── conf ├── docker-compose.yml └── linux-store-backend-passwords.yml ├── pom.xml ├── scripts ├── appstream-extractor.sh └── delete-eol-apps.py └── src ├── main ├── docker │ └── Dockerfile ├── java │ └── org │ │ └── flathub │ │ ├── LinuxStoreBackendApplication.java │ │ └── api │ │ ├── controller │ │ └── ApiController.java │ │ ├── dto │ │ ├── AppDto.java │ │ ├── AppFullDto.java │ │ ├── AppMapper.java │ │ ├── CategoryDto.java │ │ └── CategoryMapper.java │ │ ├── model │ │ ├── App.java │ │ ├── AppRelease.java │ │ ├── AppReleaseRepository.java │ │ ├── AppRepository.java │ │ ├── Arch.java │ │ ├── Category.java │ │ ├── CategoryRepository.java │ │ ├── FeedPublishBy.java │ │ ├── FlatpakRefRemoteInfo.java │ │ ├── FlatpakRepo.java │ │ ├── FlatpakRepoRepository.java │ │ ├── Screenshot.java │ │ └── ScreenshotRepository.java │ │ ├── scheduledtasks │ │ └── ScheduledTasks.java │ │ ├── service │ │ ├── ApiService.java │ │ ├── ApiServiceImpl.java │ │ ├── LocalFlatpakInstallationService.java │ │ ├── LocalFlatpakInstallationServiceImpl.java │ │ ├── SyndicationService.java │ │ ├── SyndicationServiceImpl.java │ │ ├── UpdateService.java │ │ └── UpdateServiceImpl.java │ │ └── util │ │ ├── AppdataValidationResult.java │ │ ├── AppdataValidator.java │ │ └── FlatpakRefFileCreator.java └── resources │ ├── application-DEV.yml │ ├── application-LOCAL.yml │ ├── application-PRO.yml │ ├── application-STAGING.yml │ ├── application-TEST.yml │ ├── application.properties │ └── db │ └── migration │ └── V1 │ ├── R__app_update_in_store_since_date.sql │ ├── V1.0__Initial_version.sql │ ├── V1.1__flatpakrepo_add_current_ostree_commit.sql │ ├── V1.2__app_add_icon_xxx_url.sql │ ├── V1.3__app_rename_in_store_since_date.sql │ ├── V1.4__app_rename_current_release_description.sql │ ├── V1.5__app_add_appdata_xxx_url.sql │ ├── V1.6__app_add_appdata_developer_name.sql │ └── V1.7__apprelease.sql └── test ├── java └── org │ └── flathub │ ├── ApiApplicationTests.java │ └── api │ └── service │ ├── ApiServiceImplTest.java │ └── LocalFlatpakInstallationServiceImplTest.java └── resources ├── application-TEST.yml └── application.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .classpath 3 | .project 4 | .settings 5 | .metadata 6 | build 7 | bin 8 | 9 | # STS ### 10 | .apt_generated 11 | .classpath 12 | .factorypath 13 | .project 14 | .settings 15 | .springBeans 16 | 17 | # IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | # NetBeans ### 24 | nbproject/private/ 25 | build/ 26 | nbbuild/ 27 | dist/ 28 | nbdist/ 29 | .nb-gradle/ 30 | nbactions.xml 31 | nb-configuration.xml 32 | 33 | # Spring 34 | .springBeans 35 | 36 | # Class Files # 37 | *.class 38 | 39 | # Package Files # 40 | #*.jar 41 | *.war 42 | *.ear 43 | 44 | # Maven # 45 | target/ 46 | !.mvn/wrapper/maven-wrapper.jar 47 | 48 | # Subversion files 49 | .svn 50 | 51 | # Windows 52 | [Tt]humbs.db 53 | ehthumbs.db 54 | Desktop.ini 55 | $RECYCLE.BIN/ 56 | 57 | # Linux 58 | .* 59 | !.gitignore 60 | *~ 61 | 62 | # Mac OS X 63 | .DS_Store 64 | ._.DS_Store 65 | .AppleDouble 66 | .LSOverride 67 | Icon 68 | ._* 69 | .Spotlight-V100 70 | .Trashes 71 | 72 | # Other 73 | *.bak 74 | *.log 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note: this backend has been replaced in flathub.org by [this one](https://github.com/flathub/backend/) (Sept. 2020) 2 | 3 | 4 | # Linux Store backend 5 | 6 | 7 | This is a simple Spring Boot App implementing a backend for [linux-store-frontend](https://github.com/jgarciao/linux-store-frontend): 8 | * Implements a system to get the list of apps and runtimes published in flatpak repos like [Flathub](http://flathub.org) and others 9 | * Stores the information in a relational database 10 | * Offers a simple REST API to get this information 11 | 12 | # Requirements 13 | 14 | For the backend: 15 | * Java 7+ (java-8-oracle recommended) 16 | * maven 17 | * [appstream-appdata-java](https://github.com/jgarciao/appstream-appdata-java) compiled and present in your local maven repository 18 | 19 | For the script appstream-extractor (info below): 20 | * flatpak 21 | * ostree 22 | * tar 23 | * unzip 24 | 25 | # How appstream information is obtained 26 | 27 | The list of apps and runtimes published in a flatpak repo is obtained extracting the contents of the appstream branch. 28 | 29 | This is done using the provided script appstream-extractor. It can be run like this: 30 | 31 | ``` 32 | # Add flathub repo 33 | flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 34 | 35 | # Run the extractor script as root manually. 36 | # Once it works it can be run periodically in a cron job 37 | ./appstream-extractor 38 | ``` 39 | The script will: 40 | * Extract the appstream data to /var/lib/appstream-extractor/export-data 41 | * Update the file /var/lib/appstream-extractor/appstream-extractor.info 42 | with information about the last exported data (ostree commit, ...) 43 | 44 | linux-store-backend will parse this information periodically and store it in the database. 45 | 46 | # Testing linux-store-backend 47 | 48 | You can run the backend using an in-memory database from the command line: 49 | 50 | ``` 51 | mvn install 52 | mvn spring-boot:run -Dspring.profiles.active=TEST 53 | ``` 54 | 55 | Once the service is running go to http://localhost:8080/v1/apps/update to import the appstream data exported with the script appdata-extractor. 56 | 57 | Finally, fetch the data at http://localhost:8080/v1/apps 58 | 59 | 60 | # Improving linux-store-backend 61 | 62 | This is just a proof of concept implemented with Spring Boot. Look at ApiController.java and follow the code to undertand how it works. 63 | 64 | TODO: 65 | * Improve appstream-extractor to manage multiple repos 66 | * Import and store appdata translations 67 | * Import and store appdata for all arches (arm, ...) 68 | * Search apps by name, keywords, categories, ... 69 | * Search themes 70 | * Search runtimes 71 | * Pagination 72 | * Activity: events, ... 73 | * Integration with GitHub to obtain extra info (publisher name and photo, ...) 74 | * Improve performance (ehcache, ...) 75 | * Rate Limiting 76 | -------------------------------------------------------------------------------- /conf/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | frontend: 5 | image: jgarciao/linux-store-frontend 6 | volumes: 7 | - /opt/linux-store/www/repo/:/usr/share/nginx/html/repo/ 8 | depends_on: 9 | - backend1 10 | - backend2 11 | ports: 12 | - "80:80" 13 | backend1: 14 | image: jgarciao/linux-store-backend 15 | volumes: 16 | - /opt/linux-store/:/opt/linux-store/ 17 | - /var/lib/appstream-extractor:/var/lib/appstream-extractor 18 | environment: 19 | - SPRING_PROFILES_ACTIVE=PRO,SCHED 20 | - JAVA_OPTS=-Duser.timezone=GMT -Dspring.config.location=/opt/linux-store/linux-store-backend-passwords.yml 21 | ports: 22 | - "8080" 23 | depends_on: 24 | - postgresdb 25 | backend2: 26 | image: jgarciao/linux-store-backend 27 | volumes: 28 | - /opt/linux-store/:/opt/linux-store/ 29 | - /var/lib/appstream-extractor:/var/lib/appstream-extractor 30 | environment: 31 | - SPRING_PROFILES_ACTIVE=PRO 32 | - JAVA_OPTS=-Duser.timezone=GMT -Dspring.config.location=/opt/linux-store/linux-store-backend-passwords.yml -Dflyway.enabled=false 33 | ports: 34 | - "8080" 35 | depends_on: 36 | - postgresdb 37 | postgresdb: 38 | image: postgres:9.6-alpine 39 | volumes: 40 | - /var/lib/postgresql-docker/data:/var/lib/postgresql/data 41 | ports: 42 | - "5432:5432" 43 | environment: 44 | POSTGRES_USER: linuxstore 45 | POSTGRES_DB: linuxstore 46 | -------------------------------------------------------------------------------- /conf/linux-store-backend-passwords.yml: -------------------------------------------------------------------------------- 1 | # This is an exaple file containing passwords and other secrets that we don't want to 2 | # package into the final application. More info at README.md 3 | spring: 4 | datasource: 5 | password: linuxstore 6 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.flathub 8 | linux-store-backend 9 | 0.6.8 10 | jar 11 | 12 | api 13 | RESTful API for FlatHub 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-parent 18 | 1.5.10.RELEASE 19 | 20 | 21 | 22 | 23 | UTF-8 24 | UTF-8 25 | 1.8 26 | jgarciao 27 | 1.2.0.Final 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-data-jpa 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | 48 | org.hibernate 49 | hibernate-java8 50 | 51 | 52 | 53 | com.fasterxml.jackson.datatype 54 | jackson-datatype-jsr310 55 | 56 | 57 | 58 | 59 | org.postgresql 60 | postgresql 61 | runtime 62 | 63 | 64 | 65 | org.hsqldb 66 | hsqldb 67 | runtime 68 | 69 | 70 | 71 | org.projectlombok 72 | lombok 73 | true 74 | 75 | 76 | 77 | org.freedesktop.appstream 78 | appstream-appdata 79 | 0.4.3 80 | 81 | 82 | 83 | org.rauschig 84 | jarchivelib 85 | 0.7.1 86 | 87 | 88 | 89 | com.google.guava 90 | guava 91 | 21.0 92 | 93 | 94 | 95 | org.mapstruct 96 | mapstruct-jdk8 97 | ${org.mapstruct.version} 98 | 99 | 100 | 101 | org.mapstruct 102 | mapstruct-processor 103 | ${org.mapstruct.version} 104 | true 105 | 106 | 107 | 108 | org.flywaydb 109 | flyway-core 110 | 4.2.0 111 | 112 | 113 | 114 | org.apache.commons 115 | commons-exec 116 | 1.3 117 | 118 | 119 | 120 | com.rometools 121 | rome 122 | 1.12.0 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | org.springframework.boot 133 | spring-boot-maven-plugin 134 | 135 | 136 | 137 | com.spotify 138 | docker-maven-plugin 139 | 0.4.14 140 | 141 | ${docker.image.prefix}/${project.artifactId} 142 | src/main/docker 143 | 144 | 145 | / 146 | ${project.build.directory} 147 | ${project.build.finalName}.jar 148 | 149 | 150 | 151 | 152 | 153 | 154 | org.apache.maven.plugins 155 | maven-compiler-plugin 156 | 3.5.1 157 | 158 | 1.8 159 | 1.8 160 | 161 | 162 | org.mapstruct 163 | mapstruct-processor 164 | ${org.mapstruct.version} 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /scripts/appstream-extractor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2017-2018 Jorge García Oncins 4 | # 5 | # Script to extract appstream data from flathub repo 6 | 7 | APPSTREAM_EXTRACTOR_HOME=/var/lib/appstream-extractor 8 | APPSTREAM_EXTRACTOR_DEST_FOLDER=$APPSTREAM_EXTRACTOR_HOME/export-data 9 | FLATPAK_OSTREE_REPO_PATH=$HOME/.local/share/flatpak/repo/ 10 | 11 | 12 | function check-required-programs { 13 | command -v flatpak >/dev/null 2>&1 || { echo >&2 "I require flatpak but it's not installed. Aborting."; exit 1; } 14 | command -v ostree >/dev/null 2>&1 || { echo >&2 "I require ostree but it's not installed. Aborting."; exit 1; } 15 | command -v tar >/dev/null 2>&1 || { echo >&2 "I require tar but it's not installed. Aborting."; exit 1; } 16 | command -v gunzip >/dev/null 2>&1 || { echo >&2 "I require gunzip but it's not installed. Aborting."; exit 1; } 17 | command -v find >/dev/null 2>&1 || { echo >&2 "I require find but it's not installed. Aborting."; exit 1; } 18 | } 19 | 20 | function check-required-folders { 21 | 22 | if [ ! -d "$APPSTREAM_EXTRACTOR_HOME" ]; then 23 | 24 | echo "This script requires to create this folder $APPSTREAM_EXTRACTOR_HOME" 25 | echo "Do you want to continue?" 26 | select yn in "Yes" "No"; do 27 | case $yn in 28 | Yes ) create-required-folders; 29 | break;; 30 | No ) exit;; 31 | esac 32 | done 33 | fi 34 | 35 | if [ ! -d "$APPSTREAM_EXTRACTOR_DEST_FOLDER" ]; then 36 | 37 | echo "This script requires to create this folder $APPSTREAM_EXTRACTOR_DEST_FOLDER" 38 | echo "Do you want to continue?" 39 | select yn in "Yes" "No"; do 40 | case $yn in 41 | Yes ) create-required-folders; 42 | break;; 43 | No ) exit;; 44 | esac 45 | done 46 | fi 47 | } 48 | 49 | function create-required-folders { 50 | mkdir -p $APPSTREAM_EXTRACTOR_DEST_FOLDER; 51 | if [ $? -ne 0 ]; then 52 | exit 53 | fi; 54 | } 55 | 56 | function extract-files-from-ostree { 57 | 58 | COMMIT_DATE_TEMP=`/usr/bin/ostree --repo=$FLATPAK_OSTREE_REPO_PATH show $FLATPAK_APPSTREAM_REF | grep "Date:"` 59 | COMMIT_DATE_PREFIX="Date: " 60 | COMMIT_DATE=${COMMIT_DATE_TEMP#$COMMIT_DATE_PREFIX} 61 | DATE_WITHOUT_WHITESPACES=${COMMIT_DATE// /.} 62 | 63 | APPSTREAM_EXTRACT_FILE="$APPSTREAM_EXTRACTOR_DEST_FOLDER/appstream-$FLATPAK_REMOTE_NAME-$ARCH-$DATE_WITHOUT_WHITESPACES.tar.gz" 64 | APPSTREAM_EXTRACT_FILE_UNZIP_FOLDER=${APPSTREAM_EXTRACT_FILE%".tar.gz"} 65 | FLATPAK_REMOTE_INFO_FILE="$APPSTREAM_EXTRACTOR_DEST_FOLDER/appstream-$FLATPAK_REMOTE_NAME-$ARCH-$DATE_WITHOUT_WHITESPACES-remote-info.txt" 66 | 67 | echo "REPO=$FLATPAK_REMOTE_NAME" > $REMOTE_INFO_DESCRIPTOR 68 | echo "COMMIT=$COMMIT" >> $REMOTE_INFO_DESCRIPTOR 69 | echo "DATE=$COMMIT_DATE" >> $REMOTE_INFO_DESCRIPTOR 70 | echo "FILE=$APPSTREAM_EXTRACT_FILE" >> $REMOTE_INFO_DESCRIPTOR 71 | echo "EXPORT_DATA=$APPSTREAM_EXTRACT_FILE_UNZIP_FOLDER" >> $REMOTE_INFO_DESCRIPTOR 72 | echo "REMOTE_INFO=$FLATPAK_REMOTE_INFO_FILE" >> $REMOTE_INFO_DESCRIPTOR 73 | 74 | echo "Extracting appstream info $APPSTREAM_EXTRACT_FILE" 75 | /usr/bin/ostree --repo=$FLATPAK_OSTREE_REPO_PATH export $FLATPAK_APPSTREAM_REF | /bin/gzip > $APPSTREAM_EXTRACT_FILE 76 | 77 | mkdir $APPSTREAM_EXTRACT_FILE_UNZIP_FOLDER 78 | /bin/tar -xf $APPSTREAM_EXTRACT_FILE -C $APPSTREAM_EXTRACT_FILE_UNZIP_FOLDER 79 | 80 | # Create file with last ostree commit per app and other info 81 | flatpak remote-ls --user -d $FLATPAK_REMOTE_NAME --arch $ARCH > $FLATPAK_REMOTE_INFO_FILE 82 | 83 | echo "Cleaning up unused files in $APPSTREAM_EXTRACTOR_DEST_FOLDER" 84 | find $APPSTREAM_EXTRACTOR_DEST_FOLDER/appstream-$FLATPAK_REMOTE_NAME-$ARCH* -mtime +1 -exec rm -fr {} \; 2> /dev/null 85 | 86 | } 87 | 88 | 89 | function extract-appstream-by-arch { 90 | 91 | FLATPAK_REMOTE_NAME=$1 92 | ARCH=$2 93 | 94 | FLATPAK_APPSTREAM_REF=flathub/appstream2/$ARCH 95 | REMOTE_INFO_DESCRIPTOR=$APPSTREAM_EXTRACTOR_HOME/$FLATPAK_REMOTE_NAME-$ARCH.info 96 | 97 | echo "Getting info for remote $FLATPAK_REMOTE_NAME and arch $ARCH ..." 98 | 99 | # Update appstream info from repo 100 | /usr/bin/timeout 300s /usr/bin/flatpak --user update --appstream --arch=$ARCH 101 | 102 | # Get current commit from appstream ostree log 103 | COMMIT_TEMP=`/usr/bin/ostree --repo=$FLATPAK_OSTREE_REPO_PATH show $FLATPAK_APPSTREAM_REF | grep "commit"` 104 | COMMIT_PREFIX="commit " 105 | COMMIT=${COMMIT_TEMP#$COMMIT_PREFIX} 106 | 107 | # Extract appstream-data if first run or current commit != previous commit 108 | # and update REMOTE_INFO_DESCRIPTOR file 109 | if [ ! -f $REMOTE_INFO_DESCRIPTOR ] 110 | then 111 | extract-files-from-ostree 112 | else 113 | PREVIOUS_COMMIT_TEMP=`grep COMMIT= $REMOTE_INFO_DESCRIPTOR` 114 | PREVIOUS_COMMIT_PREFIX="COMMIT=" 115 | PREVIOUS_COMMIT=${PREVIOUS_COMMIT_TEMP#$PREVIOUS_COMMIT_PREFIX} 116 | 117 | if [ "$COMMIT" != "$PREVIOUS_COMMIT" ] 118 | then 119 | extract-files-from-ostree 120 | fi 121 | fi 122 | 123 | echo 124 | 125 | } 126 | 127 | 128 | ####################################################################################################################################### 129 | 130 | # Check requirements 131 | check-required-programs 132 | check-required-folders 133 | 134 | # Configure remote 135 | flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 136 | 137 | # Extract appstream info 138 | extract-appstream-by-arch flathub x86_64 139 | extract-appstream-by-arch flathub i386 140 | extract-appstream-by-arch flathub arm 141 | extract-appstream-by-arch flathub aarch64 142 | 143 | # Clean old appstream data 144 | ostree --repo=$HOME/.local/share/flatpak/repo/ prune --refs-only --depth=0 145 | 146 | 147 | -------------------------------------------------------------------------------- /scripts/delete-eol-apps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | 6 | import psycopg2 7 | import yaml 8 | 9 | 10 | def get_eol_apps(): 11 | list_command = "flatpak remote-ls --user --all --columns=ref,options flathub" 12 | list_run = subprocess.Popen(list_command, shell=True, stdout=subprocess.PIPE, universal_newlines=True) 13 | output, _ = list_run.communicate() 14 | eol_indicators = ("eol=", "eol-rebase=") 15 | eol_apps = [] 16 | 17 | for line in output.split('\n'): 18 | if len(line): 19 | splitline = line.split('\t') 20 | if len(splitline) < 2: 21 | continue 22 | else: 23 | ref, options = splitline 24 | 25 | kind, appid, _, _ = ref.split("/") 26 | 27 | if kind != "app": 28 | continue 29 | 30 | if any(x in options for x in eol_indicators): 31 | eol_apps.append(appid) 32 | 33 | return set(eol_apps) 34 | 35 | 36 | def main(): 37 | homedir = os.path.expanduser("~") 38 | store_config_file = os.path.join(homedir, "linux-store-backend.yml") 39 | with open(store_config_file) as f: 40 | store_config = yaml.load(f) 41 | 42 | _, _, db_host, db_name = store_config['spring']['datasource']['url'].split('/') 43 | pgsql_conn = { 44 | "host": db_host.split(":")[0], 45 | "port": db_host.split(":")[1], 46 | "password": store_config['spring']['datasource']['password'], 47 | "database": db_name, 48 | } 49 | 50 | conn = psycopg2.connect(**pgsql_conn) 51 | with conn.cursor() as c: 52 | c.execute("SELECT flatpak_app_id FROM app;") 53 | store_apps = {x[0] for x in c.fetchall()} 54 | 55 | eol_apps = get_eol_apps() 56 | apps_to_delete = store_apps & eol_apps 57 | 58 | for app_id in apps_to_delete: 59 | print("=> deleting {}".format(app_id)) 60 | with conn.cursor() as c: 61 | c.execute("SELECT app_id FROM app where flatpak_app_id = %s;", (app_id,)) 62 | db_id = c.fetchone()[0] 63 | 64 | c.execute("DELETE FROM app_category WHERE app_id = %s;", (db_id,)) 65 | c.execute("DELETE FROM app_release WHERE app_id = %s;", (db_id,)) 66 | c.execute("DELETE FROM Screenshot WHERE app_id = %s;", (db_id,)) 67 | c.execute("DELETE FROM app WHERE app_id = %s;", (db_id,)) 68 | 69 | conn.commit() 70 | conn.close() 71 | 72 | if __name__ == "__main__": 73 | main() 74 | -------------------------------------------------------------------------------- /src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-slim-stretch 2 | 3 | ENV FLATPAK_GL_DRIVERS=dummy 4 | 5 | # Install flatpak from stretch-backports add the flathub repository 6 | # Run `docker build --no-cache .` to update dependencies 7 | RUN printf "deb http://ftp.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/backports.list && \ 8 | apt-get update && apt-get install --no-install-recommends -y -t stretch-backports flatpak && \ 9 | flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 10 | 11 | #Mount /tmp folder for Tomcat temp files 12 | VOLUME /tmp 13 | 14 | #Add latest build jar file 15 | ADD target/linux-store-backend-0.6.8.jar app.jar 16 | RUN sh -c 'touch /app.jar' 17 | ENV JAVA_OPTS="" 18 | ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ] 19 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/LinuxStoreBackendApplication.java: -------------------------------------------------------------------------------- 1 | package org.flathub; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.scheduling.annotation.EnableScheduling; 7 | import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; 8 | import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; 9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 10 | 11 | @SpringBootApplication 12 | @EnableScheduling 13 | public class LinuxStoreBackendApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(LinuxStoreBackendApplication.class, args); 17 | } 18 | 19 | 20 | @Configuration 21 | public static class PathMatchingConfigurationAdapter extends WebMvcConfigurerAdapter { 22 | 23 | @Override 24 | public void configurePathMatch(PathMatchConfigurer configurer) { 25 | configurer.setUseSuffixPatternMatch(false); 26 | } 27 | 28 | @Override 29 | public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { 30 | configurer.favorPathExtension(false); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/controller/ApiController.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.controller; 2 | 3 | import com.rometools.rome.io.FeedException; 4 | import org.flathub.api.dto.AppDto; 5 | import org.flathub.api.dto.AppFullDto; 6 | import org.flathub.api.dto.AppMapper; 7 | import org.flathub.api.service.ApiService; 8 | import org.flathub.api.service.UpdateService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by jorge on 24/03/17. 18 | */ 19 | @CrossOrigin(origins = {"http://localhost:4200", "http://localhost:80", "http://45.55.104.129:80"}) 20 | @RestController 21 | @RequestMapping("/api/v1") 22 | public class ApiController { 23 | 24 | @Autowired 25 | private ApiService apiService; 26 | 27 | @Autowired 28 | private UpdateService updateService; 29 | 30 | @Autowired 31 | private AppMapper mapper; 32 | 33 | @RequestMapping(value = "/apps/{flatpakAppId}", method = RequestMethod.GET) 34 | public AppFullDto findAppFlatpakAppId(@PathVariable String flatpakAppId) { 35 | return mapper.appToAppFullDto(apiService.findAppByFlatpakAppId(flatpakAppId)); 36 | } 37 | 38 | @RequestMapping(value = "/apps", method = RequestMethod.GET) 39 | public List findAll() { 40 | 41 | return mapper.appsToAppDtos(apiService.findAllApps()); 42 | } 43 | 44 | @RequestMapping(value = "/apps/category/{categoryName}", method = RequestMethod.GET) 45 | public List findAllByCategory(@PathVariable String categoryName) { 46 | return mapper.appsToAppDtos(apiService.findAllAppsByCategoryName(categoryName)); 47 | } 48 | 49 | @RequestMapping(value = "/apps/collection/{collectionName}", method = RequestMethod.GET) 50 | public List findAllByCollection(@PathVariable String collectionName) { 51 | return mapper.appsToAppDtos(apiService.findAllAppsByCollectionName(collectionName)); 52 | } 53 | 54 | @RequestMapping(value = { "/apps/collection/{collectionName}/feed","/apps/collection/{collectionName}/feed.xml"}, 55 | produces = { MediaType.APPLICATION_XML_VALUE }, 56 | method = RequestMethod.GET) 57 | @ResponseBody public String getRssFeedByCollection(HttpServletResponse response, @PathVariable String collectionName) { 58 | try { 59 | return apiService.getRssFeedByCollectionName(collectionName); 60 | } catch (FeedException e) { 61 | e.printStackTrace(); 62 | return ""; 63 | } 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/dto/AppDto.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.dto; 2 | 3 | import java.time.OffsetDateTime; 4 | 5 | /** 6 | * Created by jorge on 18/12/17. 7 | */ 8 | public class AppDto { 9 | 10 | private String flatpakAppId; 11 | private String name; 12 | private String summary; 13 | private String iconDesktopUrl; 14 | private String iconMobileUrl; 15 | private String currentReleaseVersion; 16 | private OffsetDateTime currentReleaseDate; 17 | private OffsetDateTime inStoreSinceDate; 18 | private double rating; 19 | private int ratingVotes; 20 | 21 | public String getFlatpakAppId() { 22 | return flatpakAppId; 23 | } 24 | 25 | public void setFlatpakAppId(String flatpakAppId) { 26 | this.flatpakAppId = flatpakAppId; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public String getSummary() { 38 | return summary; 39 | } 40 | 41 | public void setSummary(String summary) { 42 | this.summary = summary; 43 | } 44 | 45 | public String getIconDesktopUrl() { 46 | return iconDesktopUrl; 47 | } 48 | 49 | public void setIconDesktopUrl(String iconDesktopUrl) { 50 | this.iconDesktopUrl = iconDesktopUrl; 51 | } 52 | 53 | public String getIconMobileUrl() { 54 | return iconMobileUrl; 55 | } 56 | 57 | public void setIconMobileUrl(String iconMobileUrl) { 58 | this.iconMobileUrl = iconMobileUrl; 59 | } 60 | 61 | public double getRating() { 62 | return rating; 63 | } 64 | 65 | public void setRating(double rating) { 66 | this.rating = rating; 67 | } 68 | 69 | public int getRatingVotes() { 70 | return ratingVotes; 71 | } 72 | 73 | public void setRatingVotes(int ratingVotes) { 74 | this.ratingVotes = ratingVotes; 75 | } 76 | 77 | 78 | public String getCurrentReleaseVersion() { 79 | return currentReleaseVersion; 80 | } 81 | 82 | public void setCurrentReleaseVersion(String currentReleaseVersion) { 83 | this.currentReleaseVersion = currentReleaseVersion; 84 | } 85 | 86 | public OffsetDateTime getCurrentReleaseDate() { 87 | return currentReleaseDate; 88 | } 89 | 90 | public void setCurrentReleaseDate(OffsetDateTime currentReleaseDate) { 91 | this.currentReleaseDate = currentReleaseDate; 92 | } 93 | 94 | public OffsetDateTime getInStoreSinceDate() { 95 | return inStoreSinceDate; 96 | } 97 | 98 | public void setInStoreSinceDate(OffsetDateTime inStoreSinceDate) { 99 | this.inStoreSinceDate = inStoreSinceDate; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/dto/AppFullDto.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import java.time.OffsetDateTime; 5 | import java.util.ArrayList; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | import java.util.Set; 9 | import org.flathub.api.model.Screenshot; 10 | 11 | /** 12 | * Created by jorge on 19/12/17. 13 | */ 14 | public class AppFullDto { 15 | 16 | private String flatpakAppId; 17 | private String name; 18 | private String summary; 19 | private String description; 20 | private String developerName; 21 | private String projectLicense; 22 | private String homepageUrl; 23 | private String bugtrackerUrl; 24 | private String helpUrl; 25 | private String donationUrl; 26 | private String translateUrl; 27 | private String iconDesktopUrl; 28 | private String iconMobileUrl; 29 | private String downloadFlatpakRefUrl; 30 | private String currentReleaseVersion; 31 | private OffsetDateTime currentReleaseDate; 32 | private String currentReleaseDescription; 33 | private OffsetDateTime inStoreSinceDate; 34 | private double rating; 35 | private int ratingVotes; 36 | 37 | 38 | private Set categories = new HashSet<>(); 39 | private List screenshots = new ArrayList<>(); 40 | 41 | public String getFlatpakAppId() { 42 | return flatpakAppId; 43 | } 44 | 45 | public void setFlatpakAppId(String flatpakAppId) { 46 | this.flatpakAppId = flatpakAppId; 47 | } 48 | 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | public void setName(String name) { 54 | this.name = name; 55 | } 56 | 57 | public String getSummary() { 58 | return summary; 59 | } 60 | 61 | public void setSummary(String summary) { 62 | this.summary = summary; 63 | } 64 | 65 | public String getDescription() { 66 | return description; 67 | } 68 | 69 | public void setDescription(String description) { 70 | this.description = description; 71 | } 72 | 73 | public String getProjectLicense() { 74 | return projectLicense; 75 | } 76 | 77 | public void setProjectLicense(String projectLicense) { 78 | this.projectLicense = projectLicense; 79 | } 80 | 81 | public String getHomepageUrl() { 82 | return homepageUrl; 83 | } 84 | 85 | public void setHomepageUrl(String homepageUrl) { 86 | this.homepageUrl = homepageUrl; 87 | } 88 | 89 | public String getBugtrackerUrl() { 90 | return bugtrackerUrl; 91 | } 92 | 93 | public void setBugtrackerUrl(String bugtrackerUrl) { 94 | this.bugtrackerUrl = bugtrackerUrl; 95 | } 96 | 97 | public String getIconDesktopUrl() { 98 | return iconDesktopUrl; 99 | } 100 | 101 | public void setIconDesktopUrl(String iconDesktopUrl) { 102 | this.iconDesktopUrl = iconDesktopUrl; 103 | } 104 | 105 | public String getIconMobileUrl() { 106 | return iconMobileUrl; 107 | } 108 | 109 | public void setIconMobileUrl(String iconMobileUrl) { 110 | this.iconMobileUrl = iconMobileUrl; 111 | } 112 | 113 | public String getDownloadFlatpakRefUrl() { 114 | return downloadFlatpakRefUrl; 115 | } 116 | 117 | public void setDownloadFlatpakRefUrl(String downloadFlatpakRefUrl) { 118 | this.downloadFlatpakRefUrl = downloadFlatpakRefUrl; 119 | } 120 | 121 | public String getCurrentReleaseVersion() { 122 | return currentReleaseVersion; 123 | } 124 | 125 | public void setCurrentReleaseVersion(String currentReleaseVersion) { 126 | this.currentReleaseVersion = currentReleaseVersion; 127 | } 128 | 129 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT") 130 | public OffsetDateTime getCurrentReleaseDate() { 131 | return currentReleaseDate; 132 | } 133 | 134 | public void setCurrentReleaseDate(OffsetDateTime currentReleaseDate) { 135 | this.currentReleaseDate = currentReleaseDate; 136 | } 137 | 138 | public String getCurrentReleaseDescription() { 139 | return currentReleaseDescription; 140 | } 141 | 142 | public void setCurrentReleaseDescription(String currentReleaseDescription) { 143 | this.currentReleaseDescription = currentReleaseDescription; 144 | } 145 | 146 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT") 147 | public OffsetDateTime getInStoreSinceDate() { 148 | return inStoreSinceDate; 149 | } 150 | 151 | public void setInStoreSinceDate(OffsetDateTime inStoreSinceDate) { 152 | this.inStoreSinceDate = inStoreSinceDate; 153 | } 154 | 155 | public double getRating() { 156 | return rating; 157 | } 158 | 159 | public void setRating(double rating) { 160 | this.rating = rating; 161 | } 162 | 163 | public int getRatingVotes() { 164 | return ratingVotes; 165 | } 166 | 167 | public void setRatingVotes(int ratingVotes) { 168 | this.ratingVotes = ratingVotes; 169 | } 170 | 171 | public Set getCategories() { 172 | return categories; 173 | } 174 | 175 | public void setCategories(Set categories) { 176 | this.categories = categories; 177 | } 178 | 179 | public List getScreenshots() { 180 | return screenshots; 181 | } 182 | 183 | public void setScreenshots(List screenshots) { 184 | this.screenshots = screenshots; 185 | } 186 | 187 | public String getDeveloperName() { 188 | return developerName; 189 | } 190 | 191 | public void setDeveloperName(String developerName) { 192 | this.developerName = developerName; 193 | } 194 | 195 | public String getHelpUrl() { 196 | return helpUrl; 197 | } 198 | 199 | public void setHelpUrl(String helpUrl) { 200 | this.helpUrl = helpUrl; 201 | } 202 | 203 | public String getDonationUrl() { 204 | return donationUrl; 205 | } 206 | 207 | public void setDonationUrl(String donationUrl) { 208 | this.donationUrl = donationUrl; 209 | } 210 | 211 | public String getTranslateUrl() { 212 | return translateUrl; 213 | } 214 | 215 | public void setTranslateUrl(String translateUrl) { 216 | this.translateUrl = translateUrl; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/dto/AppMapper.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.dto; 2 | 3 | import org.flathub.api.model.App; 4 | import org.mapstruct.Mapper; 5 | import org.mapstruct.factory.Mappers; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by jorge on 18/12/17. 11 | */ 12 | @Mapper(componentModel = "spring", uses = CategoryMapper.class) 13 | public interface AppMapper { 14 | 15 | List appsToAppDtos(List apps); 16 | AppDto appToAppDto(App app); 17 | 18 | List appsToAppFullDtos(List apps); 19 | AppFullDto appToAppFullDto(App app); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/dto/CategoryDto.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.dto; 2 | 3 | public class CategoryDto { 4 | 5 | private String name; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/dto/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.dto; 2 | 3 | import java.util.List; 4 | import org.flathub.api.model.Category; 5 | import org.mapstruct.Mapper; 6 | 7 | @Mapper(componentModel = "spring") 8 | public interface CategoryMapper { 9 | 10 | List categoriesToCategoryDtos(List categories); 11 | CategoryDto categoryToCategoryDto(Category category); 12 | 13 | } -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/App.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import java.time.OffsetDateTime; 7 | import java.util.ArrayList; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.Set; 11 | import javax.persistence.Basic; 12 | import javax.persistence.CascadeType; 13 | import javax.persistence.Column; 14 | import javax.persistence.Entity; 15 | import javax.persistence.FetchType; 16 | import javax.persistence.GeneratedValue; 17 | import javax.persistence.GenerationType; 18 | import javax.persistence.Id; 19 | import javax.persistence.JoinColumn; 20 | import javax.persistence.JoinTable; 21 | import javax.persistence.ManyToMany; 22 | import javax.persistence.ManyToOne; 23 | import javax.persistence.OneToMany; 24 | import javax.persistence.SequenceGenerator; 25 | import javax.persistence.Transient; 26 | 27 | /** 28 | * Created by jorge on 04/05/17. 29 | */ 30 | 31 | @Entity 32 | public class App { 33 | 34 | 35 | public static final int APP_DESCRIPTION_LENGTH = 4096; 36 | public static final int APP_RELEASE_DESCRIPTION_LENGTH = 4096; 37 | private final String FLATPAKREF_BASE_PATH = "/repo/appstream"; 38 | private int appId; 39 | private String flatpakAppId; 40 | private String name; 41 | private String summary; 42 | private String description; 43 | private String developerName; 44 | private String projectLicense; 45 | private String homepageUrl; 46 | private String bugtrackerUrl; 47 | private String helpUrl; 48 | private String donationUrl; 49 | private String translateUrl; 50 | private String currentReleaseVersion; 51 | private OffsetDateTime currentReleaseDate; 52 | private String currentReleaseDescription; 53 | private OffsetDateTime inStoreSinceDate; 54 | private FlatpakRepo flatpakRepo; 55 | private double rating; 56 | private int ratingVotes; 57 | private String iconDesktopUrl; 58 | private String iconMobileUrl; 59 | private Set categories = new HashSet<>(); 60 | private List screenshots = new ArrayList<>(); 61 | 62 | @JsonIgnore 63 | @Id 64 | @SequenceGenerator(name = "app_app_id_seq", 65 | sequenceName = "app_app_id_seq", 66 | allocationSize = 1) 67 | @GeneratedValue(strategy = GenerationType.SEQUENCE, 68 | generator = "app_app_id_seq") 69 | @Column(name = "app_id", nullable = false) 70 | public int getAppId() { 71 | return appId; 72 | } 73 | 74 | @JsonProperty 75 | public void setAppId(int appId) { 76 | this.appId = appId; 77 | } 78 | 79 | @Basic 80 | @Column(name = "flatpak_app_id", nullable = false, length = 128) 81 | public String getFlatpakAppId() { 82 | return flatpakAppId; 83 | } 84 | 85 | public void setFlatpakAppId(String flatpakAppId) { 86 | this.flatpakAppId = flatpakAppId; 87 | } 88 | 89 | @Basic 90 | @Column(name = "name", nullable = false, length = 128) 91 | public String getName() { 92 | return name; 93 | } 94 | 95 | public void setName(String name) { 96 | this.name = name; 97 | } 98 | 99 | @Basic 100 | @Column(name = "summary", length = 1024) 101 | public String getSummary() { 102 | return summary; 103 | } 104 | 105 | public void setSummary(String summary) { 106 | this.summary = summary; 107 | } 108 | 109 | @Basic 110 | @Column(name = "description", length = APP_DESCRIPTION_LENGTH) 111 | public String getDescription() { 112 | return description; 113 | } 114 | 115 | public void setDescription(String description) { 116 | this.description = description; 117 | } 118 | 119 | @Basic 120 | @Column(name = "project_license", length = 1024) 121 | public String getProjectLicense() { 122 | return projectLicense; 123 | } 124 | 125 | public void setProjectLicense(String projectLicense) { 126 | this.projectLicense = projectLicense; 127 | } 128 | 129 | @Basic 130 | @Column(name = "homepage_url", length = 2048) 131 | public String getHomepageUrl() { 132 | return homepageUrl; 133 | } 134 | 135 | public void setHomepageUrl(String homepageUrl) { 136 | this.homepageUrl = homepageUrl; 137 | } 138 | 139 | @Basic 140 | @Column(name = "bugtracker_url", length = 2048) 141 | public String getBugtrackerUrl() { 142 | return bugtrackerUrl; 143 | } 144 | 145 | public void setBugtrackerUrl(String bugtrackerUrl) { 146 | this.bugtrackerUrl = bugtrackerUrl; 147 | } 148 | 149 | @Basic 150 | @Column(name = "current_release_version", length = 1024) 151 | public String getCurrentReleaseVersion() { 152 | return currentReleaseVersion; 153 | } 154 | 155 | public void setCurrentReleaseVersion(String currentReleaseVersion) { 156 | this.currentReleaseVersion = currentReleaseVersion; 157 | } 158 | 159 | @Column(name = "current_release_date") 160 | public OffsetDateTime getCurrentReleaseDate() { 161 | return currentReleaseDate; 162 | } 163 | 164 | public void setCurrentReleaseDate(OffsetDateTime currentReleaseDate) { 165 | this.currentReleaseDate = currentReleaseDate; 166 | } 167 | 168 | @Basic 169 | @Column(name = "current_release_description", length = APP_RELEASE_DESCRIPTION_LENGTH) 170 | public String getCurrentReleaseDescription() { 171 | return currentReleaseDescription; 172 | } 173 | 174 | public void setCurrentReleaseDescription(String currentReleaseDescription) { 175 | this.currentReleaseDescription = currentReleaseDescription; 176 | } 177 | 178 | @Column(name = "in_store_since_date") 179 | public OffsetDateTime getInStoreSinceDate() { 180 | 181 | return inStoreSinceDate; 182 | } 183 | 184 | public void setInStoreSinceDate(OffsetDateTime inStoreSinceDate) { 185 | this.inStoreSinceDate = inStoreSinceDate; 186 | } 187 | 188 | @Basic 189 | @Column(name = "rating") 190 | public double getRating() { 191 | return rating; 192 | } 193 | 194 | public void setRating(double rating) { 195 | this.rating = rating; 196 | } 197 | 198 | @Basic 199 | @Column(name = "rating_votes") 200 | public int getRatingVotes() { 201 | return ratingVotes; 202 | } 203 | 204 | public void setRatingVotes(int ratingVotes) { 205 | this.ratingVotes = ratingVotes; 206 | } 207 | 208 | @Basic 209 | @Column(name = "icon_desktop_url", length = 2048) 210 | public String getIconDesktopUrl() { 211 | return iconDesktopUrl; 212 | } 213 | 214 | public void setIconDesktopUrl(String iconDesktopUrl) { 215 | this.iconDesktopUrl = iconDesktopUrl; 216 | } 217 | 218 | @Basic 219 | @Column(name = "icon_mobile_url", length = 2048) 220 | public String getIconMobileUrl() { 221 | return iconMobileUrl; 222 | } 223 | 224 | public void setIconMobileUrl(String iconMobileUrl) { 225 | this.iconMobileUrl = iconMobileUrl; 226 | } 227 | 228 | @Basic 229 | @Column(name = "developer_name", length = 1024) 230 | public String getDeveloperName() { 231 | return developerName; 232 | } 233 | 234 | public void setDeveloperName(String developerName) { 235 | this.developerName = developerName; 236 | } 237 | 238 | @Basic 239 | @Column(name = "help_url", length = 2048) 240 | public String getHelpUrl() { 241 | return helpUrl; 242 | } 243 | 244 | public void setHelpUrl(String helpUrl) { 245 | this.helpUrl = helpUrl; 246 | } 247 | 248 | @Basic 249 | @Column(name = "donation_url", length = 2048) 250 | public String getDonationUrl() { 251 | return donationUrl; 252 | } 253 | 254 | public void setDonationUrl(String donationUrl) { 255 | this.donationUrl = donationUrl; 256 | } 257 | 258 | @Basic 259 | @Column(name = "translate_url", length = 2048) 260 | public String getTranslateUrl() { 261 | return translateUrl; 262 | } 263 | 264 | public void setTranslateUrl(String translateUrl) { 265 | this.translateUrl = translateUrl; 266 | } 267 | 268 | @JsonIgnore 269 | @ManyToOne(fetch = FetchType.LAZY) 270 | @JoinColumn(name = "flatpak_repo_id", referencedColumnName = "flatpak_repo_id") 271 | public FlatpakRepo getFlatpakRepo() { 272 | return this.flatpakRepo; 273 | } 274 | 275 | @JsonProperty 276 | public void setFlatpakRepo(FlatpakRepo repo) { 277 | this.flatpakRepo = repo; 278 | } 279 | 280 | // @JsonInclude() 281 | // @Transient 282 | // public String getIconDesktopUrl() { 283 | // return flathubIconsDesktopUrl + "/" + this.getFlatpakAppId() + ".png"; 284 | // } 285 | // 286 | // @JsonInclude() 287 | // @Transient 288 | // public String getIconMobileUrl() { 289 | // return flathubIconsMobileUrl + "/" + this.getFlatpakAppId() + ".png"; 290 | // } 291 | 292 | @JsonInclude() 293 | @Transient 294 | public String getDownloadFlatpakRefUrl() { 295 | return FLATPAKREF_BASE_PATH + "/" + this.getFlatpakAppId() + ".flatpakref"; 296 | } 297 | 298 | @ManyToMany(cascade = { 299 | CascadeType.PERSIST, 300 | CascadeType.MERGE 301 | }, fetch = FetchType.LAZY) 302 | @JoinTable(name = "app_category", 303 | joinColumns = @JoinColumn(name = "app_id"), 304 | inverseJoinColumns = @JoinColumn(name = "category_id") 305 | ) 306 | public Set getCategories() { 307 | return categories; 308 | } 309 | 310 | public void setCategories(Set categories) { 311 | this.categories = categories; 312 | } 313 | 314 | public void addCategory(Category category) { 315 | 316 | if (!this.categories.contains(category)) { 317 | categories.add(category); 318 | } 319 | 320 | } 321 | 322 | public void removeCategory(Category category) { 323 | categories.remove(category); 324 | } 325 | 326 | @OneToMany(mappedBy = "app", 327 | cascade = CascadeType.ALL, 328 | orphanRemoval = true, 329 | fetch = FetchType.LAZY) 330 | public List getScreenshots() { 331 | return screenshots; 332 | } 333 | 334 | public void setScreenshots(List screenshots) { 335 | this.screenshots = screenshots; 336 | } 337 | 338 | public void addScreenshot(Screenshot screenshot) { 339 | 340 | this.screenshots.add(screenshot); 341 | screenshot.setApp(this); 342 | 343 | } 344 | 345 | public void removeScreenshot(Screenshot screenshot) { 346 | 347 | this.screenshots.remove(screenshot); 348 | screenshot.setApp(null); 349 | } 350 | 351 | 352 | } 353 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/AppRelease.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import javax.persistence.*; 7 | import java.time.OffsetDateTime; 8 | 9 | /** 10 | * Created by jorge on 01/01/19. 11 | */ 12 | @Entity 13 | public class AppRelease { 14 | 15 | private int appReleaseId; 16 | private Arch arch; 17 | private String appdataReleaseVersion; 18 | private boolean appdataReleaseVersionUpdated; 19 | private String ostreeCommitHash; 20 | private String ostreeCommitHashParent; 21 | private OffsetDateTime ostreeCommitDate; 22 | private OffsetDateTime ostreeCommitDateNext; 23 | private String ostreeCommitSubject; 24 | private int downloads; 25 | private int updates; 26 | private int installs; 27 | private App app; 28 | private String downloadSize; 29 | private String installedSize; 30 | private String runtime; 31 | private String sdk; 32 | private boolean isEndOfLife; 33 | private String endOfLifeInfo; 34 | private String endOfLifeRebase; 35 | private String metadata; 36 | 37 | 38 | @JsonIgnore 39 | @Id 40 | @SequenceGenerator(name = "apprelease_apprelease_id_seq", 41 | sequenceName = "apprelease_apprelease_id_seq", 42 | allocationSize = 1) 43 | @GeneratedValue(strategy = GenerationType.SEQUENCE, 44 | generator = "apprelease_apprelease_id_seq") 45 | @Column(name = "apprelease_id", nullable = false) 46 | public int getAppReleaseId() { 47 | return appReleaseId; 48 | } 49 | 50 | public void setAppReleaseId(int appReleaseId) { 51 | this.appReleaseId = appReleaseId; 52 | } 53 | 54 | @Enumerated(EnumType.STRING) 55 | @Column(length = 32) 56 | @JsonIgnore 57 | public Arch getArch() { 58 | return arch; 59 | } 60 | 61 | public void setArch(Arch arch) { 62 | this.arch = arch; 63 | } 64 | 65 | public String getAppdataReleaseVersion() { 66 | return appdataReleaseVersion; 67 | } 68 | 69 | public void setAppdataReleaseVersion(String appdataReleaseVersion) { 70 | this.appdataReleaseVersion = appdataReleaseVersion; 71 | } 72 | 73 | public boolean isAppdataReleaseVersionUpdated() { 74 | return appdataReleaseVersionUpdated; 75 | } 76 | 77 | public void setAppdataReleaseVersionUpdated(boolean appdataReleaseVersionUpdated) { 78 | this.appdataReleaseVersionUpdated = appdataReleaseVersionUpdated; 79 | } 80 | 81 | public String getOstreeCommitHash() { 82 | return ostreeCommitHash; 83 | } 84 | 85 | @Transient 86 | public String getOstreeCommitShortHash() { 87 | 88 | if(ostreeCommitHash != null && ostreeCommitHash.length() > 12){ 89 | return ostreeCommitHash.substring(0,12); 90 | } 91 | else{ 92 | return null; 93 | } 94 | } 95 | 96 | public void setOstreeCommitHash(String ostreeCommitHash) { 97 | this.ostreeCommitHash = ostreeCommitHash; 98 | } 99 | 100 | public String getOstreeCommitHashParent() { 101 | return ostreeCommitHashParent; 102 | } 103 | 104 | public void setOstreeCommitHashParent(String ostreeCommitHashParent) { 105 | this.ostreeCommitHashParent = ostreeCommitHashParent; 106 | } 107 | 108 | @Transient 109 | public String getOstreeCommitShortHashParent() { 110 | 111 | if(ostreeCommitHashParent != null && ostreeCommitHashParent.length() > 12){ 112 | return ostreeCommitHashParent.substring(0,12); 113 | } 114 | else{ 115 | return null; 116 | } 117 | } 118 | 119 | public OffsetDateTime getOstreeCommitDate() { 120 | return ostreeCommitDate; 121 | } 122 | 123 | public void setOstreeCommitDate(OffsetDateTime ostreeCommitDate) { 124 | this.ostreeCommitDate = ostreeCommitDate; 125 | } 126 | 127 | public OffsetDateTime getOstreeCommitDateNext() { 128 | return ostreeCommitDateNext; 129 | } 130 | 131 | public void setOstreeCommitDateNext(OffsetDateTime ostreeCommitDateNext) { 132 | this.ostreeCommitDateNext = ostreeCommitDateNext; 133 | } 134 | 135 | public String getOstreeCommitSubject() { 136 | return ostreeCommitSubject; 137 | } 138 | 139 | public void setOstreeCommitSubject(String ostreeCommitSubject) { 140 | this.ostreeCommitSubject = ostreeCommitSubject; 141 | } 142 | 143 | public int getDownloads() { 144 | return downloads; 145 | } 146 | 147 | public void setDownloads(int downloads) { 148 | this.downloads = downloads; 149 | } 150 | 151 | public int getUpdates() { 152 | return updates; 153 | } 154 | 155 | public void setUpdates(int updates) { 156 | this.updates = updates; 157 | } 158 | 159 | public int getInstalls() { 160 | return installs; 161 | } 162 | 163 | public void setInstalls(int installs) { 164 | this.installs = installs; 165 | } 166 | 167 | public String getDownloadSize() { 168 | return downloadSize; 169 | } 170 | 171 | public void setDownloadSize(String downloadSize) { 172 | this.downloadSize = downloadSize; 173 | } 174 | 175 | public String getInstalledSize() { 176 | return installedSize; 177 | } 178 | 179 | public void setInstalledSize(String installedSize) { 180 | this.installedSize = installedSize; 181 | } 182 | 183 | public String getRuntime() { 184 | return runtime; 185 | } 186 | 187 | public void setRuntime(String runtime) { 188 | this.runtime = runtime; 189 | } 190 | 191 | public String getSdk() { 192 | return sdk; 193 | } 194 | 195 | public void setSdk(String sdk) { 196 | this.sdk = sdk; 197 | } 198 | 199 | 200 | @Column(name = "is_end_of_life") 201 | public boolean isEndOfLife() { 202 | return isEndOfLife; 203 | } 204 | 205 | public void setEndOfLife(boolean endOfLife) { 206 | isEndOfLife = endOfLife; 207 | } 208 | 209 | public String getEndOfLifeInfo() { 210 | return endOfLifeInfo; 211 | } 212 | 213 | public void setEndOfLifeInfo(String endOfLifeInfo) { 214 | this.endOfLifeInfo = endOfLifeInfo; 215 | } 216 | 217 | public String getEndOfLifeRebase() { 218 | return endOfLifeRebase; 219 | } 220 | 221 | public void setEndOfLifeRebase(String endOfLifeRebase) { 222 | this.endOfLifeRebase = endOfLifeRebase; 223 | } 224 | 225 | public String getMetadata() { 226 | return metadata; 227 | } 228 | 229 | public void setMetadata(String metadata) { 230 | this.metadata = metadata; 231 | } 232 | 233 | @JsonIgnore 234 | @ManyToOne(fetch = FetchType.LAZY) 235 | @JoinColumn(name = "app_id", referencedColumnName = "app_id") 236 | public App getApp() { 237 | return this.app; 238 | } 239 | 240 | @JsonProperty 241 | public void setApp(App app) { 242 | this.app = app; 243 | } 244 | 245 | public AppRelease() { 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/AppReleaseRepository.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by jorge on 01/01/19. 10 | */ 11 | @Repository 12 | public interface AppReleaseRepository extends JpaRepository { 13 | 14 | List findAll(); 15 | 16 | List findByAppAndArch(App app, Arch arch); 17 | 18 | AppRelease findFirstByAppAndArchOrderByOstreeCommitDateDesc(App app, Arch arch); 19 | 20 | AppRelease findOneAppReleaseByAppAndArchAndOstreeCommitHash(App app, Arch arch, String commit); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/AppRepository.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import java.time.OffsetDateTime; 4 | import java.util.List; 5 | import org.springframework.data.domain.Sort; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.query.Param; 9 | import org.springframework.stereotype.Repository; 10 | 11 | /** 12 | * Created by jorge on 24/03/17. 13 | */ 14 | @Repository 15 | public interface AppRepository extends JpaRepository { 16 | 17 | 18 | List findAll(Sort sort); 19 | 20 | List findByCategories_Name(String categoryName, Sort sort); 21 | 22 | @Query(value="select * " 23 | + "from public.app " 24 | + "where " 25 | + "(current_release_date > current_date - interval '7' day and current_release_date < current_date + interval '1' day) " 26 | + "or " 27 | + "(in_store_since_date > current_date - interval '7' day and in_store_since_date < current_date + interval '1' day) " 28 | + "order by GREATEST(coalesce(in_store_since_date, '1900-01-01 00:00:00'), " 29 | + " coalesce(current_release_date, '1900-01-01 00:00:00')) desc", 30 | nativeQuery = true) 31 | List findRecentlyAddedOrUpdated(); 32 | 33 | 34 | 35 | 36 | @Query(value="select * " 37 | + "from public.app app, public.app_release release " 38 | + "where app.app_id = release.app_id " 39 | + "and release.arch = 'X86_64' " 40 | + "and release.ostree_commit_date > current_date - interval '7' day " 41 | + "and release.ostree_commit_date_next is null " 42 | + "order by release.ostree_commit_date desc", 43 | nativeQuery = true) 44 | List findRecentlyAddedOrUpdatedUsingAppReleaseX8664(); 45 | 46 | 47 | @Query(value="select * " 48 | + "from public.app " 49 | + "where " 50 | + "(in_store_since_date > current_date - interval '7' day and in_store_since_date < current_date + interval '1' day) " 51 | + "order by coalesce(in_store_since_date, '1900-01-01 00:00:00') desc", 52 | nativeQuery = true) 53 | List findRecentlyAdded(); 54 | 55 | 56 | App findOneByFlatpakAppId(String flatpakAppId); 57 | 58 | 59 | List findAllByInStoreSinceDateAfter(OffsetDateTime minusDays, Sort sort); 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/Arch.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | @SuppressWarnings("SpellCheckingInspection") 4 | public enum Arch { 5 | X86_64, 6 | I386, 7 | ARM, 8 | AARCH64; 9 | 10 | @Override 11 | public String toString() { 12 | switch(this) { 13 | case X86_64: return "x86_64"; 14 | case I386: return "i386"; 15 | case ARM: return "arm"; 16 | case AARCH64: return "aarch64"; 17 | default: throw new IllegalArgumentException(); 18 | } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/Category.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import org.hibernate.annotations.NaturalId; 6 | 7 | import javax.persistence.*; 8 | 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | /** 13 | * Created by jorge on 20/11/17. 14 | */ 15 | @Entity 16 | public class Category { 17 | 18 | private int categoryId; 19 | private String name; 20 | private Set apps = new HashSet<>(); 21 | 22 | @JsonIgnore 23 | @Id 24 | @SequenceGenerator(name = "category_category_id_seq", 25 | sequenceName = "category_category_id_seq", 26 | allocationSize = 1) 27 | @GeneratedValue(strategy = GenerationType.SEQUENCE, 28 | generator = "category_category_id_seq") 29 | @Column(name = "category_id", nullable = false) 30 | public int getCategoryId() { 31 | return categoryId; 32 | } 33 | 34 | @JsonProperty 35 | public void setCategoryId(int categoryId) { 36 | this.categoryId = categoryId; 37 | } 38 | 39 | @NaturalId 40 | @Column(name = "name", nullable = false, length = 256) 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public void setName(String name) { 46 | this.name = name; 47 | } 48 | 49 | @JsonIgnore 50 | @ManyToMany(mappedBy = "categories", fetch = FetchType.LAZY) 51 | public Set getApps() { 52 | return apps; 53 | } 54 | 55 | public void setApps(Set apps) { 56 | this.apps = apps; 57 | } 58 | 59 | public Category() { 60 | } 61 | 62 | public Category(String name) { 63 | this.name = name; 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (this == o) return true; 69 | if (!(o instanceof Category)) return false; 70 | 71 | Category category = (Category) o; 72 | 73 | if (categoryId != category.categoryId) return false; 74 | return name.equals(category.name); 75 | } 76 | 77 | @Override 78 | public int hashCode() { 79 | int result = categoryId; 80 | result = 31 * result + name.hashCode(); 81 | return result; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/CategoryRepository.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by jorge on 21/11/17. 10 | */ 11 | @Repository 12 | public interface CategoryRepository extends JpaRepository { 13 | 14 | List findAllByOrderByName(); 15 | 16 | Category findOneByCategoryId(String categoryId); 17 | 18 | Category findOneByName(String categoryName); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/FeedPublishBy.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | public enum FeedPublishBy { 4 | AppInStoreSince, 5 | AppLastChange 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/FlatpakRefRemoteInfo.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.ArrayList; 5 | 6 | public class FlatpakRefRemoteInfo { 7 | private String ref; 8 | private String id; 9 | private String arch; 10 | private String branch; 11 | private String collection; 12 | private LocalDateTime date; 13 | private String subject; 14 | private String commit; 15 | private String shortCommit; 16 | private String parent; 17 | private String downloadSize; 18 | private String installedSize; 19 | private String runtime; 20 | private String sdk; 21 | private boolean isEndOfLife; 22 | private String endOfLife; 23 | private String endOfLifeRebase; 24 | private String metadata; 25 | private ArrayList history; 26 | 27 | public String getRef() { 28 | return ref; 29 | } 30 | 31 | public void setRef(String ref) { 32 | this.ref = ref; 33 | } 34 | 35 | public String getId() { 36 | return id; 37 | } 38 | 39 | public void setId(String id) { 40 | this.id = id; 41 | } 42 | 43 | public String getArch() { 44 | return arch; 45 | } 46 | 47 | public void setArch(String arch) { 48 | this.arch = arch; 49 | } 50 | 51 | public String getBranch() { 52 | return branch; 53 | } 54 | 55 | public void setBranch(String branch) { 56 | this.branch = branch; 57 | } 58 | 59 | public String getCollection() { 60 | return collection; 61 | } 62 | 63 | public void setCollection(String collection) { 64 | this.collection = collection; 65 | } 66 | 67 | public LocalDateTime getDate() { 68 | return date; 69 | } 70 | 71 | public void setDate(LocalDateTime date) { 72 | this.date = date; 73 | } 74 | 75 | public String getSubject() { 76 | return subject; 77 | } 78 | 79 | public void setSubject(String subject) { 80 | this.subject = subject; 81 | } 82 | 83 | public String getCommit() { 84 | return commit; 85 | } 86 | 87 | public void setCommit(String commit) { 88 | this.commit = commit; 89 | if(commit != null && commit.length() > 12){ 90 | this.shortCommit = commit.substring(0,12); 91 | } 92 | } 93 | 94 | public String getShortCommit() { 95 | return shortCommit; 96 | } 97 | 98 | public void setShortCommit(String shortCommit) { 99 | this.shortCommit = shortCommit; 100 | } 101 | 102 | public String getParent() { 103 | return parent; 104 | } 105 | 106 | public void setParent(String parent) { 107 | this.parent = parent; 108 | } 109 | 110 | public String getDownloadSize() { 111 | return downloadSize; 112 | } 113 | 114 | public void setDownloadSize(String downloadSize) { 115 | this.downloadSize = downloadSize; 116 | } 117 | 118 | public String getInstalledSize() { 119 | return installedSize; 120 | } 121 | 122 | public void setInstalledSize(String installedSize) { 123 | this.installedSize = installedSize; 124 | } 125 | 126 | public String getRuntime() { 127 | return runtime; 128 | } 129 | 130 | public void setRuntime(String runtime) { 131 | this.runtime = runtime; 132 | } 133 | 134 | public String getSdk() { 135 | return sdk; 136 | } 137 | 138 | public void setSdk(String sdk) { 139 | this.sdk = sdk; 140 | } 141 | 142 | public boolean isEndOfLife() { 143 | return isEndOfLife; 144 | } 145 | 146 | public void setEndOfLife(boolean endOfLife) { 147 | isEndOfLife = endOfLife; 148 | } 149 | 150 | public String getEndOfLife() { 151 | return endOfLife; 152 | } 153 | 154 | public void setEndOfLife(String endOfLife) { 155 | this.endOfLife = endOfLife; 156 | } 157 | 158 | public String getEndOfLifeRebase() { 159 | return endOfLifeRebase; 160 | } 161 | 162 | public void setEndOfLifeRebase(String endOfLifeRebase) { 163 | this.endOfLifeRebase = endOfLifeRebase; 164 | } 165 | 166 | public String getMetadata() { 167 | return metadata; 168 | } 169 | 170 | public void setMetadata(String metadata) { 171 | this.metadata = metadata; 172 | } 173 | 174 | public ArrayList getHistory() { 175 | return history; 176 | } 177 | 178 | public void setHistory(ArrayList history) { 179 | this.history = history; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/FlatpakRepo.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import javax.persistence.Basic; 6 | import javax.persistence.CascadeType; 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.FetchType; 10 | import javax.persistence.GeneratedValue; 11 | import javax.persistence.GenerationType; 12 | import javax.persistence.Id; 13 | 14 | import javax.persistence.OneToMany; 15 | import javax.persistence.SequenceGenerator; 16 | import javax.persistence.Table; 17 | 18 | 19 | /** 20 | * Created by jorge on 04/05/17. 21 | */ 22 | @Entity 23 | @Table(name = "flatpak_repo", schema = "public", catalog = "flathub") 24 | public class FlatpakRepo { 25 | 26 | private int flatpakRepoId; 27 | private String name; 28 | private String description; 29 | private String url; 30 | private String homepageUrl; 31 | private String downloadFlatpakRepoUrl; 32 | private String defaultBranch; 33 | private String gpgkey; 34 | private String currentOstreeCommit; 35 | private List apps; 36 | 37 | public FlatpakRepo() { 38 | this.apps = new ArrayList<>(); 39 | } 40 | 41 | @Id 42 | @SequenceGenerator(name = "flatpak_repo_flatpak_repo_id_seq", 43 | sequenceName = "flatpak_repo_flatpak_repo_id_seq", 44 | allocationSize = 1) 45 | @GeneratedValue(strategy = GenerationType.SEQUENCE, 46 | generator = "flatpak_repo_flatpak_repo_id_seq") 47 | @Column(name = "flatpak_repo_id", nullable = false) 48 | public int getFlatpakRepoId() { 49 | return flatpakRepoId; 50 | } 51 | 52 | public void setFlatpakRepoId(int flatpakRepoId) { 53 | this.flatpakRepoId = flatpakRepoId; 54 | } 55 | 56 | @Basic 57 | @Column(name = "name", nullable = false, length = 128) 58 | public String getName() { 59 | return name; 60 | } 61 | 62 | public void setName(String name) { 63 | this.name = name; 64 | } 65 | 66 | @Basic 67 | @Column(name = "description", length = 1024) 68 | public String getDescription() { 69 | return description; 70 | } 71 | 72 | public void setDescription(String description) { 73 | this.description = description; 74 | } 75 | 76 | @Basic 77 | @Column(name = "url", length = 2048) 78 | public String getUrl() { 79 | return url; 80 | } 81 | 82 | public void setUrl(String url) { 83 | this.url = url; 84 | } 85 | 86 | @Basic 87 | @Column(name = "homepage_url", length = 2048) 88 | public String getHomepageUrl() { 89 | return homepageUrl; 90 | } 91 | 92 | public void setHomepageUrl(String homepageUrl) { 93 | this.homepageUrl = homepageUrl; 94 | } 95 | 96 | @Basic 97 | @Column(name = "download_flatpakrepo_url", length = 2048) 98 | public String getDownloadFlatpakRepoUrl() { 99 | return downloadFlatpakRepoUrl; 100 | } 101 | 102 | public void setDownloadFlatpakRepoUrl(String downloadFlatpakRepoUrl) { 103 | this.downloadFlatpakRepoUrl = downloadFlatpakRepoUrl; 104 | } 105 | 106 | @Basic 107 | @Column(name = "default_branch", length = 128) 108 | public String getDefaultBranch() { 109 | return defaultBranch; 110 | } 111 | 112 | public void setDefaultBranch(String defaultBranch) { 113 | this.defaultBranch = defaultBranch; 114 | } 115 | 116 | @Basic 117 | @Column(name = "gpgkey", length = 5120) 118 | public String getGpgkey() { 119 | return gpgkey; 120 | } 121 | 122 | public void setGpgkey(String gpgkey) { 123 | this.gpgkey = gpgkey; 124 | } 125 | 126 | 127 | @Basic 128 | @Column(name = "current_ostree_commit", length = 128) 129 | public String getCurrentOstreeCommit() { 130 | return currentOstreeCommit; 131 | } 132 | 133 | public void setCurrentOstreeCommit(String currentOstreeCommit) { 134 | this.currentOstreeCommit = currentOstreeCommit; 135 | } 136 | 137 | @OneToMany(mappedBy = "flatpakRepo", 138 | cascade = CascadeType.ALL, 139 | orphanRemoval = true, 140 | fetch = FetchType.LAZY) 141 | public List getApps() { 142 | return apps; 143 | } 144 | 145 | public void setApps(List apps) { 146 | this.apps = apps; 147 | } 148 | 149 | public void addApp(App app) { 150 | this.apps.add(app); 151 | app.setFlatpakRepo(this); 152 | } 153 | 154 | @SuppressWarnings("SimplifiableIfStatement") 155 | @Override 156 | public boolean equals(Object o) { 157 | if (this == o) { 158 | return true; 159 | } 160 | if (o == null || getClass() != o.getClass()) { 161 | return false; 162 | } 163 | 164 | FlatpakRepo that = (FlatpakRepo) o; 165 | 166 | if (flatpakRepoId != that.flatpakRepoId) { 167 | return false; 168 | } 169 | if (name != null ? !name.equals(that.name) : that.name != null) { 170 | return false; 171 | } 172 | if (description != null ? !description.equals(that.description) : that.description != null) { 173 | return false; 174 | } 175 | if (url != null ? !url.equals(that.url) : that.url != null) { 176 | return false; 177 | } 178 | if (homepageUrl != null ? !homepageUrl.equals(that.homepageUrl) : that.homepageUrl != null) { 179 | return false; 180 | } 181 | if (defaultBranch != null ? !defaultBranch.equals(that.defaultBranch) 182 | : that.defaultBranch != null) { 183 | return false; 184 | } 185 | if (gpgkey != null ? !gpgkey.equals(that.gpgkey) : that.gpgkey != null) { 186 | return false; 187 | } 188 | return apps != null ? apps.equals(that.apps) : that.apps == null; 189 | } 190 | 191 | @Override 192 | public int hashCode() { 193 | int result = flatpakRepoId; 194 | result = 31 * result + (name != null ? name.hashCode() : 0); 195 | result = 31 * result + (description != null ? description.hashCode() : 0); 196 | result = 31 * result + (url != null ? url.hashCode() : 0); 197 | result = 31 * result + (homepageUrl != null ? homepageUrl.hashCode() : 0); 198 | result = 31 * result + (defaultBranch != null ? defaultBranch.hashCode() : 0); 199 | result = 31 * result + (gpgkey != null ? gpgkey.hashCode() : 0); 200 | result = 31 * result + (apps != null ? apps.hashCode() : 0); 201 | return result; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/FlatpakRepoRepository.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | /** 7 | * Created by jorge on 04/05/17. 8 | */ 9 | @Repository 10 | public interface FlatpakRepoRepository extends JpaRepository { 11 | 12 | FlatpakRepo findOneByName(String name); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/Screenshot.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import javax.persistence.*; 7 | 8 | /** 9 | * Created by jorge on 08/12/17. 10 | */ 11 | @Entity 12 | public class Screenshot { 13 | 14 | private int screenshotId; 15 | private String thumbUrl; 16 | private String imgMobileUrl; 17 | private String imgDesktopUrl; 18 | private App app; 19 | 20 | @JsonIgnore 21 | @Id 22 | @SequenceGenerator(name = "screenshot_screenshot_id_seq", 23 | sequenceName = "screenshot_screenshot_id_seq", 24 | allocationSize = 1) 25 | @GeneratedValue(strategy = GenerationType.SEQUENCE, 26 | generator = "screenshot_screenshot_id_seq") 27 | @Column(name = "screenshot_id", nullable = false) 28 | public int getScreenshotId() { 29 | return screenshotId; 30 | } 31 | 32 | @JsonProperty 33 | public void setScreenshotId(int screenshotId) { 34 | this.screenshotId = screenshotId; 35 | } 36 | 37 | @Basic 38 | @Column(name = "thumb_url", length = 2048) 39 | public String getThumbUrl() { 40 | return thumbUrl; 41 | } 42 | 43 | public void setThumbUrl(String thumbUrl) { 44 | this.thumbUrl = thumbUrl; 45 | } 46 | 47 | @Basic 48 | @Column(name = "img_mobile_url", length = 2048) 49 | public String getImgMobileUrl() { 50 | return imgMobileUrl; 51 | } 52 | 53 | public void setImgMobileUrl(String imgMobileUrl) { 54 | this.imgMobileUrl = imgMobileUrl; 55 | } 56 | 57 | @Basic 58 | @Column(name = "img_desktop_url", length = 2048) 59 | public String getImgDesktopUrl() { 60 | return imgDesktopUrl; 61 | } 62 | 63 | public void setImgDesktopUrl(String imgDesktopUrl) { 64 | this.imgDesktopUrl = imgDesktopUrl; 65 | } 66 | 67 | @JsonIgnore 68 | @ManyToOne(fetch = FetchType.LAZY) 69 | @JoinColumn(name = "app_id", referencedColumnName = "app_id") 70 | public App getApp() { 71 | return this.app; 72 | } 73 | 74 | @JsonProperty 75 | public void setApp(App app) { 76 | this.app = app; 77 | } 78 | 79 | public Screenshot() { 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/model/ScreenshotRepository.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.model; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by jorge on 10/12/17. 10 | */ 11 | @Repository 12 | public interface ScreenshotRepository extends JpaRepository { 13 | 14 | List findAll(); 15 | void deleteScrenshotsByApp(App app); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/scheduledtasks/ScheduledTasks.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.scheduledtasks; 2 | 3 | import org.flathub.api.service.UpdateService; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Profile; 8 | import org.springframework.context.event.ContextRefreshedEvent; 9 | import org.springframework.context.event.EventListener; 10 | import org.springframework.scheduling.annotation.Scheduled; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | @Component 15 | @Transactional 16 | @Profile("SCHED") 17 | public class ScheduledTasks { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class); 20 | 21 | @Autowired 22 | private UpdateService updateService; 23 | 24 | // Run at application start 25 | // @EventListener(ContextRefreshedEvent.class) 26 | // public void contextRefreshedEvent() { 27 | // 28 | // try { 29 | // updateService.updateFlathubInfo(true); 30 | // } 31 | // catch (Exception e){ 32 | // logger.error("Error updating repo info", e); 33 | // } 34 | // } 35 | 36 | @Scheduled(cron = "0 */10 * * * *") 37 | public void updateFlathubInfo() { 38 | 39 | try { 40 | updateService.updateFlathubInfo(false); 41 | } 42 | catch (Exception e){ 43 | logger.error("Error updating repo info", e); 44 | } 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/service/ApiService.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | import java.util.List; 4 | 5 | import com.rometools.rome.feed.rss.Channel; 6 | import com.rometools.rome.io.FeedException; 7 | import org.flathub.api.model.*; 8 | 9 | /** 10 | * Created by jorge on 24/03/17. 11 | */ 12 | public interface ApiService { 13 | 14 | 15 | /** Categories */ 16 | void updateCategory(Category category); 17 | 18 | Category findCategoryByName(String categoryName); 19 | 20 | 21 | /** Apps */ 22 | void updateApp(App app); 23 | 24 | List findAllApps(); 25 | 26 | List findAllAppsByCategoryName(String categoryName); 27 | 28 | List findAllAppsByCollectionName(String collectionName); 29 | 30 | App findAppByFlatpakAppId(String flatpakAppId); 31 | 32 | 33 | /** App Releases */ 34 | void updateAppRelease(AppRelease appRelease); 35 | 36 | List findAppReleaseByAppAndArch(App app, Arch arch); 37 | 38 | AppRelease findLastAppReleaseByAppAndArch(App app, Arch x8664); 39 | 40 | AppRelease findOneAppReleaseByAppAndArchAndOstreeCommitHash(App app, Arch arch, String commit); 41 | 42 | 43 | /** Repos */ 44 | void updateFlatpakRepo(FlatpakRepo repo); 45 | 46 | FlatpakRepo findRepoByName(String name); 47 | 48 | 49 | /** Screenshots */ 50 | void updateScreenshot(Screenshot screenshot); 51 | 52 | void deleteScrenshotsByApp(App app); 53 | 54 | /** RSS Feeds */ 55 | String getRssFeedByCollectionName(String collectionName) throws FeedException; 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/service/ApiServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | import com.rometools.rome.io.FeedException; 4 | import org.flathub.api.model.*; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.data.domain.Sort; 7 | import org.springframework.data.domain.Sort.Direction; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.time.OffsetDateTime; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by jorge on 24/03/17. 16 | */ 17 | @Service 18 | public class ApiServiceImpl implements ApiService { 19 | 20 | public static final String COLLECTION_NAME_RECENTLY_UPDATED = "recently-updated"; 21 | public static final String COLLECTION_NAME_NEW = "new"; 22 | public static final int COLLECTION_NAME_NEW_DAYSBACK = 30; 23 | 24 | @Autowired 25 | private AppRepository appRepository; 26 | 27 | @Autowired 28 | private CategoryRepository categoryRepository; 29 | 30 | @Autowired 31 | private ScreenshotRepository screenshotRepository; 32 | 33 | @Autowired 34 | private FlatpakRepoRepository repoRepository; 35 | 36 | @Autowired 37 | private AppReleaseRepository appReleaseRepository; 38 | 39 | @Autowired 40 | private SyndicationService syndicationService; 41 | 42 | @Override 43 | public List findAllApps() { 44 | Sort.Order order = new Sort.Order(Direction.ASC, "name").ignoreCase(); 45 | return appRepository.findAll(new Sort(order)); 46 | } 47 | 48 | @Override 49 | public List findAllAppsByCategoryName(String categoryName) { 50 | Sort.Order order = new Sort.Order(Direction.ASC, "name").ignoreCase(); 51 | return appRepository.findByCategories_Name(categoryName, new Sort(order)); 52 | } 53 | 54 | @Override 55 | public List findAllAppsByCollectionName(String collectionName) { 56 | 57 | if (COLLECTION_NAME_RECENTLY_UPDATED.equalsIgnoreCase(collectionName)) { 58 | 59 | //return appRepository.findRecentlyAddedOrUpdated(); 60 | return appRepository.findRecentlyAddedOrUpdatedUsingAppReleaseX8664(); 61 | 62 | } else if (COLLECTION_NAME_NEW.equalsIgnoreCase(collectionName)) { 63 | 64 | Sort.Order order = new Sort.Order(Direction.DESC, "InStoreSinceDate").nullsLast(); 65 | return appRepository.findAllByInStoreSinceDateAfter(OffsetDateTime.now().minusDays(COLLECTION_NAME_NEW_DAYSBACK), new Sort (order)); 66 | 67 | } else { 68 | return new ArrayList<>(); 69 | } 70 | } 71 | 72 | 73 | @Override 74 | public App findAppByFlatpakAppId(String flatpakAppId) { 75 | return appRepository.findOneByFlatpakAppId(flatpakAppId); 76 | } 77 | 78 | @Override 79 | public List findAppReleaseByAppAndArch(App app, Arch arch) { 80 | 81 | return appReleaseRepository.findByAppAndArch(app, arch); 82 | } 83 | 84 | @Override 85 | public void updateCategory(Category category) { 86 | categoryRepository.save(category); 87 | } 88 | 89 | @Override 90 | public Category findCategoryByName(String categoryName) { 91 | return categoryRepository.findOneByName(categoryName); 92 | } 93 | 94 | 95 | 96 | @Override 97 | public void updateApp(App app) { 98 | appRepository.save(app); 99 | } 100 | 101 | @Override 102 | public void updateFlatpakRepo(FlatpakRepo repo) { 103 | repoRepository.save(repo); 104 | } 105 | 106 | @Override 107 | public void updateScreenshot(Screenshot screenshot) { 108 | screenshotRepository.save(screenshot); 109 | } 110 | 111 | @Override 112 | public void deleteScrenshotsByApp(App app) { 113 | screenshotRepository.deleteScrenshotsByApp(app); 114 | } 115 | 116 | @Override 117 | public void updateAppRelease(AppRelease appRelease) { 118 | appReleaseRepository.save(appRelease); 119 | } 120 | 121 | @Override 122 | public AppRelease findLastAppReleaseByAppAndArch(App app, Arch arch) { 123 | return appReleaseRepository.findFirstByAppAndArchOrderByOstreeCommitDateDesc(app,arch); 124 | } 125 | 126 | @Override 127 | public AppRelease findOneAppReleaseByAppAndArchAndOstreeCommitHash(App app, Arch arch, String commit) { 128 | return appReleaseRepository.findOneAppReleaseByAppAndArchAndOstreeCommitHash(app,arch,commit); 129 | } 130 | 131 | @Override 132 | public FlatpakRepo findRepoByName(String name) { 133 | return repoRepository.findOneByName(name); 134 | } 135 | 136 | 137 | @Override 138 | public String getRssFeedByCollectionName(String collectionName) throws FeedException { 139 | 140 | List apps = this.findAllAppsByCollectionName(collectionName); 141 | 142 | if (COLLECTION_NAME_RECENTLY_UPDATED.equalsIgnoreCase(collectionName)) { 143 | 144 | return syndicationService.createFeed("Flathub - Updated apps", 145 | "Applications updated in Flathub in the last 7 days", 146 | "https://flathub.org/apps/collection/recently-updated", 147 | "https://flathub.org/assets/themes/flathub/flathub-logo.png", 148 | "Linux", 149 | apps, FeedPublishBy.AppLastChange); 150 | 151 | 152 | } else if (COLLECTION_NAME_NEW.equalsIgnoreCase(collectionName)) { 153 | 154 | return syndicationService.createFeed("Flathub - New apps", 155 | "New applications published in Flathub in the last " + ApiServiceImpl.COLLECTION_NAME_NEW_DAYSBACK + " days", 156 | "https://flathub.org/apps/collection/new", 157 | "https://flathub.org/assets/themes/flathub/flathub-logo.png", 158 | "Linux", 159 | apps, FeedPublishBy.AppInStoreSince); 160 | 161 | } 162 | 163 | return ""; 164 | 165 | 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/service/LocalFlatpakInstallationService.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | import org.flathub.api.model.Arch; 4 | import org.flathub.api.model.FlatpakRefRemoteInfo; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface LocalFlatpakInstallationService { 10 | 11 | Optional getQuickBasicRemoteInfoByRemoteAndArchAndId(String remote, Arch arch, String id); 12 | 13 | List getAllQuickBasicRemoteInfoByRemote(String remote); 14 | 15 | Optional getRemoteInfoByRemoteAndArchAndId(String remote, Arch arch, String id, boolean retryIfFailed); 16 | 17 | Optional getRemoteInfoByRemoteAndArchAndId(String remote, Arch arch, String id); 18 | 19 | Optional getRemoteMetatataByRemoteAndArchAndId(String remote, Arch arch, String id); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/service/LocalFlatpakInstallationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | import org.apache.commons.exec.CommandLine; 4 | import org.apache.commons.exec.DefaultExecutor; 5 | import org.apache.commons.exec.ExecuteWatchdog; 6 | import org.apache.commons.exec.PumpStreamHandler; 7 | import org.flathub.api.model.Arch; 8 | import org.flathub.api.model.FlatpakRefRemoteInfo; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.io.ByteArrayOutputStream; 15 | import java.time.LocalDateTime; 16 | import java.time.format.DateTimeFormatter; 17 | import java.util.*; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | 21 | @Service 22 | public class LocalFlatpakInstallationServiceImpl implements LocalFlatpakInstallationService { 23 | 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(LocalFlatpakInstallationServiceImpl.class); 26 | 27 | private static final int[] FLATPAK_EXIT_VALUES = {0, 1}; 28 | 29 | 30 | private static final String REMOTE_INFO_REF = "Ref:"; 31 | private static final String REMOTE_INFO_ID = "ID:"; 32 | private static final String REMOTE_INFO_ARCH = "Arch:"; 33 | private static final String REMOTE_INFO_BRANCH_ = "Branch:"; 34 | private static final String REMOTE_INFO_COLLECTION = "Collection:"; 35 | private static final String REMOTE_INFO_COLLECTION_ID = "Collection ID:"; 36 | private static final String REMOTE_INFO_DATE = "Date:"; 37 | private static final String REMOTE_INFO_SUBJECT = "Subject:"; 38 | private static final String REMOTE_INFO_COMMIT = "Commit:"; 39 | private static final String REMOTE_INFO_PARENT = "Parent:"; 40 | private static final String REMOTE_INFO_DOWNLOAD_SIZE = "Download size:"; 41 | private static final String REMOTE_INFO_INSTALLED_SIZE = "Installed size:"; 42 | private static final String REMOTE_INFO_DOWNLOAD = "Download:"; 43 | private static final String REMOTE_INFO_INSTALLED = "Installed:"; 44 | private static final String REMOTE_INFO_RUNTIME = "Runtime:"; 45 | private static final String REMOTE_INFO_SDK = "Sdk:"; 46 | private static final String REMOTE_INFO_HISTORY = "History:"; 47 | private static final String REMOTE_INFO_EOL_MESSAGE_PREFIX= "eol="; 48 | private static final String REMOTE_INFO_EOL= "End-of-life:"; 49 | private static final String REMOTE_INFO_EOL_REBASE= "End-of-life-rebase:"; 50 | 51 | 52 | 53 | @SuppressWarnings("unused") 54 | @Value("${flatpak.flatpak-command}") 55 | private String flatpakCommand; 56 | 57 | private String[] remoteInfoCache; 58 | private LocalDateTime remoteInfoCacheDate; 59 | 60 | @SuppressWarnings("OptionalIsPresent") 61 | @Override 62 | public List getAllQuickBasicRemoteInfoByRemote(String remote) { 63 | 64 | String command = flatpakCommand + " --system remote-ls --columns=ref:f,commit:f,installed-size:f,download-size:f,options:f --arch=* " + remote; 65 | 66 | Optional remoteInfo; 67 | ArrayList remoteInfoList = new ArrayList<>(); 68 | 69 | LOGGER.debug("Getting quick basic remote info for all refs in remote " + remote); 70 | 71 | try { 72 | 73 | if (remoteInfoCache == null || remoteInfoCacheDate == null || 74 | remoteInfoCacheDate.isBefore(LocalDateTime.now().minusMinutes(5))) { 75 | 76 | String result = execToString(command); 77 | remoteInfoCache = result.split("[\\r\\n]+"); 78 | remoteInfoCacheDate = LocalDateTime.now(); 79 | 80 | } 81 | 82 | for (String tabulatedRemoteInfo : remoteInfoCache) { 83 | 84 | remoteInfo = this.parseBasicRemoteInfoLine(tabulatedRemoteInfo); 85 | 86 | if (remoteInfo.isPresent()) { 87 | remoteInfoList.add(remoteInfo.get()); 88 | } 89 | 90 | } 91 | 92 | return remoteInfoList; 93 | } catch (Exception e) { 94 | LOGGER.error("Error getting basic remote info for all refs in remote " + remote + ": ", e); 95 | return remoteInfoList; 96 | } 97 | } 98 | 99 | public Optional getQuickBasicRemoteInfoByRemoteAndArchAndId(String remote, Arch arch, String id) { 100 | 101 | String command = flatpakCommand + " --system remote-ls --columns=ref:f,commit:f,installed-size:f,download-size:f,options:f --arch=* " + remote; 102 | 103 | Optional remoteInfo; 104 | 105 | LOGGER.debug("Getting quick basic remote info for id " + id + "for arch " + arch + " and remote " + remote); 106 | 107 | try { 108 | 109 | if (remoteInfoCache == null || remoteInfoCacheDate == null || 110 | remoteInfoCacheDate.isBefore(LocalDateTime.now().minusMinutes(5))) { 111 | 112 | String result = execToString(command); 113 | remoteInfoCache = result.split("[\\r\\n]+"); 114 | remoteInfoCacheDate = LocalDateTime.now(); 115 | 116 | } 117 | 118 | for (String tabulatedRemoteInfo : remoteInfoCache) { 119 | 120 | remoteInfo = this.parseBasicRemoteInfoLine(tabulatedRemoteInfo); 121 | 122 | if (remoteInfo.isPresent() && remoteInfo.get().getId().equalsIgnoreCase(id) && 123 | remoteInfo.get().getArch().equalsIgnoreCase(arch.toString())) { 124 | return remoteInfo; 125 | } 126 | 127 | } 128 | 129 | return Optional.empty(); 130 | } catch (Exception e) { 131 | LOGGER.error("There was an error getting quick basic remote info for id " + id + " and arch " + arch + " and remote " + remote, e); 132 | return Optional.empty(); 133 | } 134 | } 135 | 136 | @Override 137 | public Optional getRemoteInfoByRemoteAndArchAndId(String remote, Arch arch, String id, boolean retryIfFailed) { 138 | 139 | 140 | Optional completeRemoteInfo; 141 | 142 | completeRemoteInfo = this.getRemoteInfoByRemoteAndArchAndId(remote, arch, id); 143 | 144 | if (!completeRemoteInfo.isPresent() && retryIfFailed) { 145 | 146 | try { 147 | LOGGER.warn("Waiting 5 secs to try get the remote-info again for " + id + " ..."); 148 | Thread.sleep(5000); 149 | 150 | completeRemoteInfo = this.getRemoteInfoByRemoteAndArchAndId(remote, arch, id); 151 | 152 | } catch (InterruptedException e) { 153 | LOGGER.error("There was an error while waiting to execute remote-info", e); 154 | } 155 | } 156 | 157 | return completeRemoteInfo; 158 | } 159 | 160 | 161 | 162 | @Override 163 | public Optional getRemoteInfoByRemoteAndArchAndId(String remote, Arch arch, String id) { 164 | 165 | String command = flatpakCommand + " --system remote-info --log --arch " + arch.toString() + " " + remote + " " + id; 166 | String result; 167 | 168 | LOGGER.debug("Getting remote info for id " + id + " and arch " + arch + " and remote " + remote); 169 | 170 | try{ 171 | 172 | Optional quickInfo = getQuickBasicRemoteInfoByRemoteAndArchAndId(remote, arch, id); 173 | 174 | Optional completeInfo; 175 | 176 | if(quickInfo.isPresent()){ 177 | 178 | result = execToString(command); 179 | completeInfo = parseRemoteInfoString(result); 180 | 181 | if(completeInfo.isPresent()){ 182 | 183 | //Wait 300 ms to try to avoid server-side errors 184 | Thread.sleep(300); 185 | 186 | Optional metadata = getRemoteMetatataByRemoteAndArchAndId(remote, arch, id); 187 | if(metadata.isPresent()){ 188 | completeInfo.get().setMetadata(metadata.get()); 189 | } 190 | 191 | return completeInfo; 192 | } 193 | } 194 | 195 | return Optional.empty(); 196 | 197 | } 198 | catch (Exception e){ 199 | LOGGER.error("There was an error getting remote info for id " + id + " for arch " + arch + " and remote " + remote, e); 200 | return Optional.empty(); 201 | } 202 | 203 | } 204 | 205 | 206 | 207 | @Override 208 | public Optional getRemoteMetatataByRemoteAndArchAndId(String remote, Arch arch, String id) { 209 | 210 | String command = flatpakCommand + " --system remote-info --show-metadata --arch " + arch.toString() + " " + remote + " " + id; 211 | String result; 212 | 213 | LOGGER.debug("Getting remote metadata for id " + id + "for arch " + arch + " and remote " + remote); 214 | 215 | try{ 216 | result = execToString(command); 217 | return Optional.ofNullable(result); 218 | } 219 | catch (Exception e){ 220 | LOGGER.error("There was an error getting remote metadata for id " + id + " and arch " + arch + " and remote " + remote, e); 221 | return Optional.empty(); 222 | } 223 | 224 | } 225 | 226 | 227 | private Optional parseBasicRemoteInfoLine(String tabulatedRemoteInfo) { 228 | 229 | FlatpakRefRemoteInfo remoteInfo = new FlatpakRefRemoteInfo(); 230 | String[] columns = tabulatedRemoteInfo.trim().replace("\t\t","\t").split("[\\t]"); 231 | 232 | if (columns.length > 3) { 233 | 234 | remoteInfo.setRef(columns[0]); 235 | 236 | remoteInfo.setBranch(getBranchFromRef(remoteInfo.getRef())); 237 | remoteInfo.setId(getIDFromRef(remoteInfo.getRef())); 238 | remoteInfo.setArch(getArchFromRef(remoteInfo.getRef())); 239 | 240 | remoteInfo.setShortCommit(columns[1]); 241 | remoteInfo.setInstalledSize(columns[2]); 242 | remoteInfo.setDownloadSize(columns[3]); 243 | 244 | if (columns.length > 4 && 245 | columns[4] != null && !"".equalsIgnoreCase(columns[4]) && columns[4].startsWith(REMOTE_INFO_EOL_MESSAGE_PREFIX)) { 246 | remoteInfo.setEndOfLife(columns[4].replace(REMOTE_INFO_EOL_MESSAGE_PREFIX, "")); 247 | remoteInfo.setEndOfLife(true); 248 | } 249 | else{ 250 | remoteInfo.setEndOfLife(false); 251 | } 252 | 253 | 254 | return Optional.of(remoteInfo); 255 | } else { 256 | return Optional.empty(); 257 | } 258 | 259 | } 260 | 261 | 262 | private String getIDFromRef(String ref){ 263 | 264 | String[] fields = ref.split("[/]"); 265 | 266 | if(fields.length == 4){ 267 | return fields[1]; 268 | } 269 | return ref; 270 | } 271 | 272 | private String getArchFromRef(String ref){ 273 | 274 | String[] fields = ref.split("[/]"); 275 | 276 | if(fields.length == 4){ 277 | return fields[2]; 278 | } 279 | return ref; 280 | } 281 | 282 | private String getBranchFromRef(String ref){ 283 | 284 | String[] fields = ref.split("[/]"); 285 | 286 | if(fields.length == 4){ 287 | return fields[3]; 288 | } 289 | return ref; 290 | } 291 | 292 | private String execToString(String command) throws Exception { 293 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 294 | CommandLine commandline = CommandLine.parse(command); 295 | DefaultExecutor exec = new DefaultExecutor(); 296 | exec.setExitValues(FLATPAK_EXIT_VALUES); 297 | PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream); 298 | exec.setStreamHandler(streamHandler); 299 | 300 | ExecuteWatchdog watchdog = new ExecuteWatchdog(20000); 301 | exec.setWatchdog(watchdog); 302 | int exitValue = exec.execute(commandline); 303 | 304 | if (exitValue != 0) { 305 | 306 | if(watchdog.killedProcess()){ 307 | // it was killed on purpose by the watchdog 308 | throw new TimeoutException(); 309 | } 310 | else { 311 | throw new Exception(outputStream.toString()); 312 | } 313 | 314 | } 315 | 316 | return(outputStream.toString()); 317 | } 318 | 319 | private Optional parseRemoteInfoString(String remoteInfoString){ 320 | 321 | String[] lines = remoteInfoString.split("[\\r\\n]+"); 322 | String line; 323 | 324 | FlatpakRefRemoteInfo remoteInfo = new FlatpakRefRemoteInfo(); 325 | 326 | remoteInfo.setEndOfLife(false); 327 | 328 | int lineCount = 0; 329 | 330 | while(lineCount parseOstreeHistory(FlatpakRefRemoteInfo currentRemoteInfo, String[] history) { 402 | 403 | ArrayList list = new ArrayList<>(); 404 | Optional currentParsedRemoteInfo; 405 | String currentParsedRemoteInfoString; 406 | 407 | int lineCount = 0; 408 | while(lineCount(list.subList(i+1, list.size()))); 445 | } 446 | 447 | return list; 448 | } 449 | 450 | 451 | 452 | } 453 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/service/SyndicationService.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | import com.rometools.rome.io.FeedException; 4 | import org.flathub.api.model.App; 5 | import org.flathub.api.model.FeedPublishBy; 6 | 7 | import java.util.List; 8 | 9 | public interface SyndicationService { 10 | 11 | String createFeed(String feedTitle, String feedDescription, String feedHomePageUrl, String feedIconUrl, 12 | String feedCategory, List apps, FeedPublishBy publishBy) throws FeedException; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/service/SyndicationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | 4 | import com.rometools.rome.feed.synd.*; 5 | import com.rometools.rome.io.FeedException; 6 | import com.rometools.rome.io.SyndFeedOutput; 7 | import org.flathub.api.model.*; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Date; 13 | import java.util.List; 14 | 15 | @Service 16 | public class SyndicationServiceImpl implements SyndicationService { 17 | 18 | 19 | @Autowired 20 | private AppRepository appRepository; 21 | 22 | @Autowired 23 | private AppReleaseRepository appReleaseRepository; 24 | 25 | 26 | public String createFeed(String feedTitle, String feedDescription, String feedHomePageUrl, String feedIconUrl, 27 | String feedCategory, List apps, FeedPublishBy publishBy) throws FeedException { 28 | 29 | SyndFeed feed = new SyndFeedImpl(); 30 | feed.setFeedType("rss_2.0"); 31 | feed.setTitle(feedTitle); 32 | feed.setLink(feedHomePageUrl); 33 | feed.setDescription(feedDescription); 34 | 35 | SyndImage image = new SyndImageImpl(); 36 | image.setUrl(feedIconUrl); 37 | feed.setIcon(image); 38 | 39 | List categories = new ArrayList<>(); 40 | SyndCategory category = new SyndCategoryImpl(); 41 | category.setName(feedCategory); 42 | categories.add(category); 43 | 44 | List entries = new ArrayList<>(); 45 | SyndEntry entry; 46 | AppRelease appRelease; 47 | String descriptionContents; 48 | 49 | for(App app: apps){ 50 | 51 | entry = new SyndEntryImpl(); 52 | entry.setTitle(app.getName()); 53 | 54 | //TODO: do not harcode this url here 55 | entry.setLink("https://flathub.org/apps/details/" + app.getFlatpakAppId()); 56 | 57 | SyndContent description = new SyndContentImpl(); 58 | description.setType("text/html"); 59 | 60 | descriptionContents = ""; 61 | 62 | if(app.getIconDesktopUrl() != null && app.getIconDesktopUrl().length() > 0){ 63 | 64 | if(app.getIconDesktopUrl().startsWith("http")){ 65 | descriptionContents = ""; 66 | } 67 | else{ 68 | descriptionContents = ""; 69 | } 70 | 71 | } 72 | 73 | if(app.getSummary() != null && app.getSummary().length() > 0){ 74 | descriptionContents = descriptionContents + "

" + app.getSummary() + "

"; 75 | } 76 | 77 | if(app.getDescription() != null && app.getDescription().length() > 0){ 78 | descriptionContents = descriptionContents + app.getDescription() + "
"; 79 | } 80 | 81 | if(app.getScreenshots() != null && app.getScreenshots().size()>0){ 82 | descriptionContents = descriptionContents + "

"; 83 | } 84 | 85 | 86 | descriptionContents = descriptionContents + "

Additional information:

"; 87 | descriptionContents = descriptionContents + "
    "; 88 | 89 | if(app.getCurrentReleaseVersion() != null && app.getCurrentReleaseVersion().length() > 0){ 90 | descriptionContents = descriptionContents + "
  • Version: " + app.getCurrentReleaseVersion() + "
  • "; 91 | } 92 | else{ 93 | descriptionContents = descriptionContents + "
  • Version: —\n
  • "; 94 | } 95 | 96 | if(app.getDeveloperName() != null && app.getDeveloperName().length() > 0){ 97 | descriptionContents = descriptionContents + "
  • Developer: " + app.getDeveloperName() + "
  • "; 98 | } 99 | else{ 100 | descriptionContents = descriptionContents + "
  • Developer: —\n
  • "; 101 | } 102 | 103 | if(app.getProjectLicense() != null && app.getProjectLicense().length() > 0){ 104 | if(app.getProjectLicense().contains("LicenseRef-proprietary")){ 105 | descriptionContents = descriptionContents + "
  • License: Proprietary
  • "; 106 | } 107 | else{ 108 | descriptionContents = descriptionContents + "
  • License: " + app.getProjectLicense() + "
  • "; 109 | } 110 | } 111 | else{ 112 | descriptionContents = descriptionContents + "
  • License: —\n
  • "; 113 | } 114 | 115 | // Close the "Additional info" section 116 | descriptionContents = descriptionContents + "
"; 117 | 118 | appRelease = appReleaseRepository.findFirstByAppAndArchOrderByOstreeCommitDateDesc(app, Arch.X86_64); 119 | if(appRelease != null){ 120 | descriptionContents = descriptionContents + "

Latest changes:

"; 121 | descriptionContents = descriptionContents + "
    "; 122 | descriptionContents = descriptionContents + "
  • Subject: " + appRelease.getOstreeCommitSubject() + "
  • "; 123 | descriptionContents = descriptionContents + "
  • Date: " + appRelease.getOstreeCommitDate() + "
  • "; 124 | descriptionContents = descriptionContents + "
  • Hash: " + appRelease.getOstreeCommitShortHash() + "
  • "; 125 | descriptionContents = descriptionContents + "
"; 126 | } 127 | 128 | if(publishBy == FeedPublishBy.AppLastChange){ 129 | 130 | if(appRelease != null && appRelease.getOstreeCommitDate() != null){ 131 | entry.setPublishedDate(Date.from(appRelease.getOstreeCommitDate().toInstant())); 132 | } 133 | else if(app.getCurrentReleaseDate() != null){ 134 | entry.setPublishedDate(Date.from(app.getCurrentReleaseDate().toInstant())); 135 | } 136 | 137 | } 138 | else if(publishBy == FeedPublishBy.AppInStoreSince){ 139 | entry.setPublishedDate(Date.from(app.getInStoreSinceDate().toInstant())); 140 | } 141 | 142 | description.setValue(descriptionContents); 143 | entry.setDescription(description); 144 | 145 | entry.setCategories(categories); 146 | 147 | 148 | entries.add(entry); 149 | 150 | } 151 | 152 | feed.setEntries(entries); 153 | 154 | return new SyndFeedOutput().outputString(feed); 155 | 156 | } 157 | 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/service/UpdateService.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | /** 4 | * Created by jorge on 20/05/17. 5 | */ 6 | public interface UpdateService { 7 | 8 | void updateFlathubInfo(boolean forceUpdate); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/util/AppdataValidationResult.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.util; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class AppdataValidationResult { 6 | 7 | private boolean valid; 8 | private final ArrayList issues = new ArrayList<>(); 9 | 10 | 11 | public boolean isValid() { 12 | return valid; 13 | } 14 | 15 | public void setValid(boolean valid) { 16 | this.valid = valid; 17 | } 18 | 19 | public ArrayList getIssues() { 20 | return issues; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/util/AppdataValidator.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.util; 2 | 3 | import org.flathub.api.model.App; 4 | import org.freedesktop.appstream.AppdataComponent; 5 | 6 | public class AppdataValidator { 7 | 8 | //FIXME: define this just in one place 9 | private static final String APPSTREAM_TYPE_DESKTOP = "desktop"; 10 | 11 | 12 | /** 13 | * Check if the component contains the required appdata: 14 | * - For all components: FlatpakId, Name, Summary 15 | * - For apps require also: Description, Icon 16 | *

17 | * Check data size limits: 18 | * - description.length < App.APP_DESCRIPTION_LENGTH 19 | * 20 | * @return true if the component has the required info for its type and respects the size limits 21 | */ 22 | public static AppdataValidationResult validateAppdataInformation(AppdataComponent component, 23 | String iconBaseRelativePath, 24 | short iconHeightDefault, 25 | short iconHeightHidpi) { 26 | 27 | AppdataValidationResult appdataValidationResult = new AppdataValidationResult(); 28 | 29 | appdataValidationResult.setValid(true); 30 | 31 | if (component.getFlatpakId() == null || "".equalsIgnoreCase(component.getFlatpakId())) { 32 | appdataValidationResult.setValid(false); 33 | appdataValidationResult.getIssues().add("component.id is null or empty"); 34 | } 35 | 36 | if (component.findDefaultName() == null || "".equalsIgnoreCase(component.findDefaultName())) { 37 | appdataValidationResult.setValid(false); 38 | appdataValidationResult.getIssues().add("component.name is null or empty"); 39 | } 40 | 41 | if (component.findDefaultSummary() == null || "".equalsIgnoreCase(component.findDefaultSummary())) { 42 | appdataValidationResult.setValid(false); 43 | appdataValidationResult.getIssues().add("component.summary is null or empty"); 44 | } 45 | 46 | if (APPSTREAM_TYPE_DESKTOP.equalsIgnoreCase(component.getType())) { 47 | 48 | if (component.findDefaultDescription() == null) { 49 | //Add as an issue but not critical (still valid) 50 | appdataValidationResult.getIssues().add("component.description is null or empty"); 51 | } else { 52 | if (component.findDefaultDescription().length() > App.APP_DESCRIPTION_LENGTH) { 53 | appdataValidationResult.setValid(false); 54 | appdataValidationResult.getIssues().add("component.description is longuer than " + App.APP_DESCRIPTION_LENGTH); 55 | } 56 | } 57 | 58 | 59 | String hiDpiIconUrl = component.findIconUrl(iconBaseRelativePath, iconHeightHidpi); 60 | String defaultIconUrl = component.findIconUrl(iconBaseRelativePath, iconHeightDefault); 61 | 62 | if ("".equalsIgnoreCase(hiDpiIconUrl) && "".equalsIgnoreCase(defaultIconUrl)) { 63 | appdataValidationResult.setValid(false); 64 | appdataValidationResult.getIssues().add("component.icon is null"); 65 | } 66 | 67 | } 68 | 69 | return appdataValidationResult; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/flathub/api/util/FlatpakRefFileCreator.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.util; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.nio.file.Files; 7 | import java.nio.file.Paths; 8 | import org.flathub.api.model.App; 9 | 10 | /** 11 | * Created by jorge on 04/05/17. 12 | */ 13 | public class FlatpakRefFileCreator { 14 | 15 | 16 | public static void createFlatpakRefFile(String path, App app) throws IOException { 17 | 18 | //Use try-with-resource to get auto-closeable writer instance 19 | try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(path), StandardCharsets.UTF_8)) { 20 | writer.write("[Flatpak Ref]" + "\n"); 21 | writer.write("Name=" + app.getFlatpakAppId() + "\n"); 22 | writer.write("Branch=" + app.getFlatpakRepo().getDefaultBranch() + "\n"); 23 | writer.write("Title=" + app.getName() + " from " + app.getFlatpakRepo().getName() + "\n"); 24 | writer.write("Url=" + app.getFlatpakRepo().getUrl() + "\n"); 25 | writer.write("SuggestRemoteName=" + app.getFlatpakRepo().getName() + "\n"); 26 | writer.write("RuntimeRepo=" + app.getFlatpakRepo().getDownloadFlatpakRepoUrl() + "\n"); 27 | writer.write("IsRuntime=false" + "\n"); 28 | writer.write("GPGKey=" + app.getFlatpakRepo().getGpgkey() + "\n"); 29 | //writer.write("Homepage=" + app.getHomepageUrl() + "\n"); 30 | //writer.write("Comment=" + app.getSummary() + "\n"); 31 | //writer.write("Icon=" + app.getIcon()); 32 | } 33 | 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/application-DEV.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | datasource: 4 | url: jdbc:postgresql://localhost:5432/linuxstore 5 | username: linuxstore 6 | # Passwords are stored in a external configuration file 7 | # More information at README.md 8 | driver-class-name: org.postgresql.Driver 9 | jpa: 10 | ## This is important 11 | # Hibernate ddl auto (create, create-drop, validate, update) 12 | hibernate.ddl-auto: validate 13 | show-sql: false 14 | # mail: 15 | # host: smtp.gmail.com 16 | # port: 587 17 | # username: 18 | # password: 19 | # smtp.auth: true 20 | # smtp.starttls.enable: true 21 | 22 | 23 | hibernate: 24 | showSql: true 25 | dialect: org.hibernate.dialect.PostgreSQL94Dialect 26 | 27 | flathub: 28 | appstream-extractor-info: /var/lib/appstream-extractor/flathub-x86_64.info 29 | appstream-extractor-info-legacy: /var/lib/appstream-extractor/appstream-extractor.info 30 | update-service: 31 | icons: 32 | import-icons: true 33 | dest-path: /opt/linux-store/www/repo/appstream/x86_64/icons/ 34 | desktop-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/128x128/ 35 | mobile-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/64x64/ 36 | flatpakref: 37 | generate-flatpakref: true 38 | dest-path: /opt/linux-store/www/repo/appstream/ 39 | 40 | flatpak: 41 | flatpak-command: flatpak-spawn --host /usr/bin/flatpak 42 | #flatpak-command: /home/jorge/local/bin/flatpak -------------------------------------------------------------------------------- /src/main/resources/application-LOCAL.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | datasource: 4 | url: jdbc:postgresql://postgresdb:5432/linuxstore 5 | username: linuxstore 6 | # Passwords are stored in a external configuration file 7 | # More information at README.md 8 | driver-class-name: org.postgresql.Driver 9 | jpa: 10 | ## This is important 11 | # Hibernate ddl auto (create, create-drop, validate, update) 12 | hibernate.ddl-auto: validate 13 | show-sql: true 14 | # mail: 15 | # host: smtp.gmail.com 16 | # port: 587 17 | # username: 18 | # password: 19 | # smtp.auth: true 20 | # smtp.starttls.enable: true 21 | 22 | 23 | hibernate: 24 | showSql: true 25 | dialect: org.hibernate.dialect.PostgreSQL94Dialect 26 | 27 | flathub: 28 | appstream-extractor-info: /var/lib/appstream-extractor/flathub-x86_64.info 29 | appstream-extractor-info-legacy: /var/lib/appstream-extractor/appstream-extractor.info 30 | update-service: 31 | icons: 32 | import-icons: true 33 | dest-path: /opt/linux-store/www/repo/appstream/x86_64/icons/ 34 | desktop-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/128x128/ 35 | mobile-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/64x64/ 36 | flatpakref: 37 | generate-flatpakref: true 38 | dest-path: /opt/linux-store/www/repo/appstream/ 39 | 40 | flatpak: 41 | flatpak-command: /usr/bin/flatpak -------------------------------------------------------------------------------- /src/main/resources/application-PRO.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | datasource: 4 | url: jdbc:postgresql://localhost:5432/linuxstore 5 | username: linuxstore 6 | # Passwords are stored in a external configuration file 7 | # More information at README.md 8 | driver-class-name: org.postgresql.Driver 9 | jpa: 10 | ## This is important 11 | # Hibernate ddl auto (create, create-drop, validate, update) 12 | hibernate.ddl-auto: validate 13 | show-sql: false 14 | # mail: 15 | # host: smtp.gmail.com 16 | # port: 587 17 | # username: 18 | # password: 19 | # smtp.auth: true 20 | # smtp.starttls.enable: true 21 | 22 | 23 | hibernate: 24 | showSql: true 25 | dialect: org.hibernate.dialect.PostgreSQL94Dialect 26 | 27 | flathub: 28 | appstream-extractor-info: /var/lib/appstream-extractor/flathub-x86_64.info 29 | appstream-extractor-info-legacy: /var/lib/appstream-extractor/appstream-extractor.info 30 | update-service: 31 | icons: 32 | import-icons: false 33 | dest-path: /opt/linux-store/www/repo/appstream/x86_64/icons/ 34 | desktop-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/128x128/ 35 | mobile-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/64x64/ 36 | flatpakref: 37 | generate-flatpakref: false 38 | dest-path: /opt/linux-store/www/repo/appstream/ 39 | 40 | flatpak: 41 | flatpak-command: /usr/bin/flatpak -------------------------------------------------------------------------------- /src/main/resources/application-STAGING.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | datasource: 4 | url: jdbc:postgresql://postgresdb:5432/linuxstore 5 | username: linuxstore 6 | # Passwords are stored in a external configuration file 7 | # More information at README.md 8 | driver-class-name: org.postgresql.Driver 9 | jpa: 10 | ## This is important 11 | # Hibernate ddl auto (create, create-drop, validate, update) 12 | hibernate.ddl-auto: validate 13 | show-sql: false 14 | # mail: 15 | # host: smtp.gmail.com 16 | # port: 587 17 | # username: 18 | # password: 19 | # smtp.auth: true 20 | # smtp.starttls.enable: true 21 | 22 | 23 | hibernate: 24 | showSql: true 25 | dialect: org.hibernate.dialect.PostgreSQL94Dialect 26 | 27 | flathub: 28 | appstream-extractor-info: /var/lib/appstream-extractor/flathub-x86_64.info 29 | appstream-extractor-info-legacy: /var/lib/appstream-extractor/appstream-extractor.info 30 | update-service: 31 | icons: 32 | import-icons: true 33 | dest-path: /opt/linux-store/www/repo/appstream/x86_64/icons/ 34 | desktop-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/128x128/ 35 | mobile-import-path: /opt/linux-store/www/repo/appstream/x86_64/icons/64x64/ 36 | flatpakref: 37 | generate-flatpakref: true 38 | dest-path: /opt/linux-store/www/repo/appstream/ 39 | 40 | flatpak: 41 | flatpak-command: /usr/bin/flatpak -------------------------------------------------------------------------------- /src/main/resources/application-TEST.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | datasource: 4 | url: jdbc:hsqldb:mem:flathub 5 | username: sa 6 | driver-class-name: org.hsqldb.jdbcDriver 7 | #data: classpath*:data.sql 8 | jpa: 9 | database: HSQL 10 | # Create BD tables automatically 11 | hibernate: 12 | ddl-auto: create 13 | show-sql: true 14 | # mail: 15 | # host: smtp.gmail.com 16 | # port: 587 17 | # username: 18 | # password: 19 | # smtp.auth: true 20 | # smtp.starttls.enable: true 21 | 22 | 23 | hibernate: 24 | showSql: true 25 | dialect: org.hibernate.dialect.org.hibernate.dialect.HSQLDialect 26 | 27 | flathub: 28 | appstream-extractor-info: /var/lib/appstream-extractor/flathub-x86_64.info 29 | appstream-extractor-info-legacy: /var/lib/appstream-extractor/appstream-extractor.info 30 | # flatpakref: 31 | # server-path: /var/www/main-store/apps/ 32 | # url: http://localhost:80/main-store/apps/ 33 | # icons: 34 | # server-path: /var/www/main-store/icons/ 35 | # url: http://localhost:80/main-store/icons/ 36 | 37 | flatpak: 38 | flatpak-command: flatpak-spawn --host /usr/bin/flatpak 39 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Set default profile 2 | spring.profiles.active=DEV 3 | # Set default error page 4 | server.error.whitelabel.enabled=true 5 | # Add flathubapi tot the url 6 | # localhost:8080/flathubapi 7 | # server.context-path:/flathubapi 8 | # Make Jackson serialize dates in ISO format (2016-04-29T11:46:59.670+0000) 9 | # instead of timestamp (1461930419670) 10 | spring.jackson.serialization.write_dates_as_timestamps=false 11 | ##################################################################### 12 | # Logs configuration: 13 | # +info a http://blog.netgloo.com/2014/12/11/logging-in-spring-boot/ 14 | # 15 | # Level for loggers on classes inside the root package "flathub" 16 | # (and its sub-packages) 17 | # Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF 18 | logging.level.org.flathub=INFO 19 | # Specify the level for spring boot and hibernate's loggers 20 | logging.level.org.springframework.boot.autoconfigure.security=INFO 21 | logging.level.org.springframework.web=INFO 22 | logging.level.org.hibernate=ERROR 23 | # Log file location (in addition to the console) 24 | logging.file=linux-store-backend.log 25 | 26 | 27 | flyway.baseline-version=1.0 28 | flyway.baseline-on-migrate=yes 29 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/R__app_update_in_store_since_date.sql: -------------------------------------------------------------------------------- 1 | UPDATE public.app SET in_store_since_date = '2017-11-07T15:41:04Z' WHERE flatpak_app_id = 'org.gnome.Rhythmbox3'; 2 | UPDATE public.app SET in_store_since_date = '2017-06-27T20:06:14Z' WHERE flatpak_app_id = 'org.geogebra.GeoGebra'; 3 | UPDATE public.app SET in_store_since_date = '2018-03-15T11:04:16Z' WHERE flatpak_app_id = 'org.flarerpg.Flare'; 4 | UPDATE public.app SET in_store_since_date = '2017-04-13T11:48:34Z' WHERE flatpak_app_id = 'org.supertuxproject.SuperTux'; 5 | UPDATE public.app SET in_store_since_date = '2017-05-03T14:02:17Z' WHERE flatpak_app_id = 'org.darktable.Darktable'; 6 | UPDATE public.app SET in_store_since_date = '2018-03-16T04:59:36Z' WHERE flatpak_app_id = 'org.flightgear.FlightGear'; 7 | UPDATE public.app SET in_store_since_date = '2017-06-23T13:58:51Z' WHERE flatpak_app_id = 'org.libretro.RetroArch'; 8 | UPDATE public.app SET in_store_since_date = '2017-10-17T07:29:12Z' WHERE flatpak_app_id = 'net.sourceforge.Klavaro'; 9 | UPDATE public.app SET in_store_since_date = '2017-11-02T16:57:16Z' WHERE flatpak_app_id = 'com.frac_tion.teleport'; 10 | UPDATE public.app SET in_store_since_date = '2018-03-09T18:04:51Z' WHERE flatpak_app_id = 'net.ankiweb.Anki'; 11 | UPDATE public.app SET in_store_since_date = '2017-11-28T16:24:26Z' WHERE flatpak_app_id = 'com.github.geigi.cozy'; 12 | UPDATE public.app SET in_store_since_date = '2017-09-19T08:23:59Z' WHERE flatpak_app_id = 'org.godotengine.Godot'; 13 | UPDATE public.app SET in_store_since_date = '2017-05-16T23:03:40Z' WHERE flatpak_app_id = 'net.sourceforge.Ri-li'; 14 | UPDATE public.app SET in_store_since_date = '2018-01-11T18:22:29Z' WHERE flatpak_app_id = 'org.ethereum.Mist'; 15 | UPDATE public.app SET in_store_since_date = '2017-10-17T07:14:16Z' WHERE flatpak_app_id = 'io.github.Freedoom-Phase-1'; 16 | UPDATE public.app SET in_store_since_date = '2017-10-03T08:54:29Z' WHERE flatpak_app_id = 'org.gnome.Geary'; 17 | UPDATE public.app SET in_store_since_date = '2017-12-17T19:52:11Z' WHERE flatpak_app_id = 'org.gnome.tetravex'; 18 | UPDATE public.app SET in_store_since_date = '2017-12-01T10:11:21Z' WHERE flatpak_app_id = 'net.openra.OpenRA'; 19 | UPDATE public.app SET in_store_since_date = '2017-12-21T10:11:25Z' WHERE flatpak_app_id = 'com.github.z.Cumulonimbus'; 20 | UPDATE public.app SET in_store_since_date = '2018-03-16T05:00:33Z' WHERE flatpak_app_id = 'org.qbittorrent.qBittorrent'; 21 | UPDATE public.app SET in_store_since_date = '2018-01-08T03:16:19Z' WHERE flatpak_app_id = 'io.ark.Desktop'; 22 | UPDATE public.app SET in_store_since_date = '2017-11-07T21:34:07Z' WHERE flatpak_app_id = 'org.openmw.OpenMW'; 23 | UPDATE public.app SET in_store_since_date = '2017-11-10T10:22:10Z' WHERE flatpak_app_id = 'org.gnome.Devhelp'; 24 | UPDATE public.app SET in_store_since_date = '2017-05-31T16:46:58Z' WHERE flatpak_app_id = 'org.gnome.Polari'; 25 | UPDATE public.app SET in_store_since_date = '2017-04-12T20:33:23Z' WHERE flatpak_app_id = 'org.blender.Blender'; 26 | UPDATE public.app SET in_store_since_date = '2017-05-26T07:39:19Z' WHERE flatpak_app_id = 'com.tux4kids.tuxmath'; 27 | UPDATE public.app SET in_store_since_date = '2018-03-06T09:11:28Z' WHERE flatpak_app_id = 'com.github.hluk.copyq'; 28 | UPDATE public.app SET in_store_since_date = '2017-11-08T20:24:31Z' WHERE flatpak_app_id = 'com.googleplaymusicdesktopplayer.GPMDP'; 29 | UPDATE public.app SET in_store_since_date = '2017-04-26T06:13:55Z' WHERE flatpak_app_id = 'org.seul.pingus'; 30 | UPDATE public.app SET in_store_since_date = '2017-10-13T11:46:12Z' WHERE flatpak_app_id = 'org.gnome.Builder'; 31 | UPDATE public.app SET in_store_since_date = '2017-07-29T17:05:55Z' WHERE flatpak_app_id = 'org.gnome.eog'; 32 | UPDATE public.app SET in_store_since_date = '2017-11-07T15:08:02Z' WHERE flatpak_app_id = 'org.gnome.Characters'; 33 | UPDATE public.app SET in_store_since_date = '2017-05-26T07:22:14Z' WHERE flatpak_app_id = 'com.tux4kids.tuxtype'; 34 | UPDATE public.app SET in_store_since_date = '2017-04-11T06:57:06Z' WHERE flatpak_app_id = 'org.gnome.Recipes'; 35 | UPDATE public.app SET in_store_since_date = '2018-01-31T10:53:55Z' WHERE flatpak_app_id = 'org.vim.Vim'; 36 | UPDATE public.app SET in_store_since_date = '2018-01-08T15:55:23Z' WHERE flatpak_app_id = 'org.kde.krita'; 37 | UPDATE public.app SET in_store_since_date = '2018-01-31T11:00:28Z' WHERE flatpak_app_id = 'io.github.mki1967.mki3dgame'; 38 | UPDATE public.app SET in_store_since_date = '2018-01-31T11:01:37Z' WHERE flatpak_app_id = 'net.bartkessels.getit'; 39 | UPDATE public.app SET in_store_since_date = '2017-10-13T16:46:23Z' WHERE flatpak_app_id = 'org.gimp.GIMP'; 40 | UPDATE public.app SET in_store_since_date = '2017-07-22T00:10:00Z' WHERE flatpak_app_id = 'com.github.philip_scott.notes-up'; 41 | UPDATE public.app SET in_store_since_date = '2017-12-06T14:35:29Z' WHERE flatpak_app_id = 'org.kicad_pcb.KiCad'; 42 | UPDATE public.app SET in_store_since_date = '2017-12-12T22:31:37Z' WHERE flatpak_app_id = 'org.gnome.quadrapassel'; 43 | UPDATE public.app SET in_store_since_date = '2017-05-16T23:36:35Z' WHERE flatpak_app_id = 'org.armagetronad.ArmagetronAdvanced'; 44 | UPDATE public.app SET in_store_since_date = '2017-08-17T17:34:15Z' WHERE flatpak_app_id = 'org.filezillaproject.Filezilla'; 45 | UPDATE public.app SET in_store_since_date = '2017-11-07T15:13:14Z' WHERE flatpak_app_id = 'org.gnome.Dictionary'; 46 | UPDATE public.app SET in_store_since_date = '2017-10-05T14:51:18Z' WHERE flatpak_app_id = 'org.pitivi.Pitivi'; 47 | UPDATE public.app SET in_store_since_date = '2017-09-24T12:21:45Z' WHERE flatpak_app_id = 'org.sparkleshare.SparkleShare'; 48 | UPDATE public.app SET in_store_since_date = '2017-12-17T19:51:08Z' WHERE flatpak_app_id = 'org.gnome.Genius'; 49 | UPDATE public.app SET in_store_since_date = '2017-11-12T20:42:12Z' WHERE flatpak_app_id = 'im.riot.Riot'; 50 | UPDATE public.app SET in_store_since_date = '2017-04-19T17:17:55Z' WHERE flatpak_app_id = 'nl.openoffice.bluefish'; 51 | UPDATE public.app SET in_store_since_date = '2017-05-03T19:08:18Z' WHERE flatpak_app_id = 'org.gnome.Nautilus'; 52 | UPDATE public.app SET in_store_since_date = '2017-12-04T16:19:16Z' WHERE flatpak_app_id = 'net.mediaarea.BWFMetaEdit'; 53 | UPDATE public.app SET in_store_since_date = '2017-11-09T16:10:39Z' WHERE flatpak_app_id = 'org.gnome.Totem'; 54 | UPDATE public.app SET in_store_since_date = '2017-04-15T18:26:57Z' WHERE flatpak_app_id = 'org.tuxpaint.Tuxpaint'; 55 | UPDATE public.app SET in_store_since_date = '2017-05-12T05:30:25Z' WHERE flatpak_app_id = 'org.gnome.Lollypop'; 56 | UPDATE public.app SET in_store_since_date = '2017-08-10T06:22:51Z' WHERE flatpak_app_id = 'com.transmissionbt.Transmission'; 57 | UPDATE public.app SET in_store_since_date = '2017-09-06T10:58:33Z' WHERE flatpak_app_id = 'io.github.EndlessSky.endless-sky'; 58 | UPDATE public.app SET in_store_since_date = '2017-06-02T06:36:09Z' WHERE flatpak_app_id = 'org.gna.Warmux'; 59 | UPDATE public.app SET in_store_since_date = '2017-10-17T07:24:53Z' WHERE flatpak_app_id = 'org.gnome.clocks'; 60 | UPDATE public.app SET in_store_since_date = '2017-10-13T17:43:52Z' WHERE flatpak_app_id = 'de.manuel_kehl.go-for-it'; 61 | UPDATE public.app SET in_store_since_date = '2017-08-04T18:19:06Z' WHERE flatpak_app_id = 'ca.desrt.dconf-editor'; 62 | UPDATE public.app SET in_store_since_date = '2017-10-03T22:20:48Z' WHERE flatpak_app_id = 'org.telegram.desktop'; 63 | UPDATE public.app SET in_store_since_date = '2017-04-11T16:13:22Z' WHERE flatpak_app_id = 'io.github.Pithos'; 64 | UPDATE public.app SET in_store_since_date = '2017-06-01T10:19:15Z' WHERE flatpak_app_id = 'io.gitlab.osslugaru.Lugaru'; 65 | UPDATE public.app SET in_store_since_date = '2017-04-12T20:34:51Z' WHERE flatpak_app_id = 'org.musicbrainz.Picard'; 66 | UPDATE public.app SET in_store_since_date = '2017-05-12T05:25:06Z' WHERE flatpak_app_id = 'net.sourceforge.torcs'; 67 | UPDATE public.app SET in_store_since_date = '2017-11-10T10:28:11Z' WHERE flatpak_app_id = 'org.gnome.Epiphany'; 68 | UPDATE public.app SET in_store_since_date = '2017-12-05T12:26:11Z' WHERE flatpak_app_id = 'com.dosbox.DOSBox'; 69 | UPDATE public.app SET in_store_since_date = '2017-06-17T23:41:23Z' WHERE flatpak_app_id = 'com.valvesoftware.Steam'; 70 | UPDATE public.app SET in_store_since_date = '2017-06-28T08:25:28Z' WHERE flatpak_app_id = 'com.albiononline.AlbionOnline'; 71 | UPDATE public.app SET in_store_since_date = '2017-07-31T15:25:09Z' WHERE flatpak_app_id = 'org.gnome.gedit'; 72 | UPDATE public.app SET in_store_since_date = '2017-12-11T10:02:15Z' WHERE flatpak_app_id = 'cx.ring.Ring'; 73 | UPDATE public.app SET in_store_since_date = '2017-05-29T13:15:08Z' WHERE flatpak_app_id = 'org.gnome.Eolie'; 74 | UPDATE public.app SET in_store_since_date = '2017-04-14T20:46:25Z' WHERE flatpak_app_id = 'net.sourceforge.chromium-bsu'; 75 | UPDATE public.app SET in_store_since_date = '2017-10-18T10:34:22Z' WHERE flatpak_app_id = 'org.octave.Octave'; 76 | UPDATE public.app SET in_store_since_date = '2017-04-26T06:08:18Z' WHERE flatpak_app_id = 'org.frozen_bubble.frozen-bubble'; 77 | UPDATE public.app SET in_store_since_date = '2017-05-12T05:21:12Z' WHERE flatpak_app_id = 'net.sourceforge.btanks'; 78 | UPDATE public.app SET in_store_since_date = '2017-04-26T06:25:35Z' WHERE flatpak_app_id = 'org.debian.TuxPuck'; 79 | UPDATE public.app SET in_store_since_date = '2017-11-10T10:08:25Z' WHERE flatpak_app_id = 'org.gnome.Weather'; 80 | UPDATE public.app SET in_store_since_date = '2017-10-17T07:27:15Z' WHERE flatpak_app_id = 'fr.free.Homebank'; 81 | UPDATE public.app SET in_store_since_date = '2017-06-27T20:08:53Z' WHERE flatpak_app_id = 'org.gnome.frogr'; 82 | UPDATE public.app SET in_store_since_date = '2018-03-08T11:20:45Z' WHERE flatpak_app_id = 'net.fsuae.FS-UAE'; 83 | UPDATE public.app SET in_store_since_date = '2017-05-08T14:09:41Z' WHERE flatpak_app_id = 'io.mgba.mGBA'; 84 | UPDATE public.app SET in_store_since_date = '2017-05-08T18:17:28Z' WHERE flatpak_app_id = 'org.audacityteam.Audacity'; 85 | UPDATE public.app SET in_store_since_date = '2017-12-06T18:50:25Z' WHERE flatpak_app_id = 'io.lbry.lbry-app'; 86 | UPDATE public.app SET in_store_since_date = '2018-02-05T11:29:05Z' WHERE flatpak_app_id = 'com.github.cassidyjames.dippi'; 87 | UPDATE public.app SET in_store_since_date = '2017-04-11T14:07:36Z' WHERE flatpak_app_id = 'com.vinszent.GnomeTwitch'; 88 | UPDATE public.app SET in_store_since_date = '2017-11-09T13:13:44Z' WHERE flatpak_app_id = 'org.gnome.Books'; 89 | UPDATE public.app SET in_store_since_date = '2017-12-13T11:07:34Z' WHERE flatpak_app_id = 'org.gnome.Aisleriot'; 90 | UPDATE public.app SET in_store_since_date = '2017-05-15T17:04:49Z' WHERE flatpak_app_id = 'org.wesnoth.Wesnoth'; 91 | UPDATE public.app SET in_store_since_date = '2017-04-14T21:05:30Z' WHERE flatpak_app_id = 'net.sourceforge.mars-game'; 92 | UPDATE public.app SET in_store_since_date = '2017-12-18T09:03:46Z' WHERE flatpak_app_id = 'io.github.betaflight.BetaflightConfigurator'; 93 | UPDATE public.app SET in_store_since_date = '2018-01-25T22:49:11Z' WHERE flatpak_app_id = 'com.github.fabiocolacio.marker'; 94 | UPDATE public.app SET in_store_since_date = '2017-09-14T15:23:49Z' WHERE flatpak_app_id = 'com.snes9x.Snes9x'; 95 | UPDATE public.app SET in_store_since_date = '2017-09-19T21:41:52Z' WHERE flatpak_app_id = 'org.gottcode.FocusWriter'; 96 | UPDATE public.app SET in_store_since_date = '2017-05-12T05:27:34Z' WHERE flatpak_app_id = 'net.sourceforge.atanks'; 97 | UPDATE public.app SET in_store_since_date = '2017-10-04T19:29:44Z' WHERE flatpak_app_id = 'org.gnome.Hitori'; 98 | UPDATE public.app SET in_store_since_date = '2018-03-12T19:33:31Z' WHERE flatpak_app_id = 'org.jamovi.jamovi'; 99 | UPDATE public.app SET in_store_since_date = '2017-11-09T13:50:29Z' WHERE flatpak_app_id = 'org.gnome.Documents'; 100 | UPDATE public.app SET in_store_since_date = '2017-11-07T15:22:03Z' WHERE flatpak_app_id = 'org.gnome.Maps'; 101 | UPDATE public.app SET in_store_since_date = '2018-02-26T07:43:40Z' WHERE flatpak_app_id = 'net.sf.VICE'; 102 | UPDATE public.app SET in_store_since_date = '2017-09-08T21:52:05Z' WHERE flatpak_app_id = 'org.videolan.VLC'; 103 | UPDATE public.app SET in_store_since_date = '2017-10-25T19:39:29Z' WHERE flatpak_app_id = 'org.tordini.flavio.Minitube'; 104 | UPDATE public.app SET in_store_since_date = '2017-11-09T17:00:06Z' WHERE flatpak_app_id = 'org.quassel_irc.QuasselClient'; 105 | UPDATE public.app SET in_store_since_date = '2017-04-27T15:22:59Z' WHERE flatpak_app_id = 'org.inkscape.Inkscape'; 106 | UPDATE public.app SET in_store_since_date = '2017-09-26T07:36:40Z' WHERE flatpak_app_id = 'com.skype.Client'; 107 | UPDATE public.app SET in_store_since_date = '2017-10-25T21:27:46Z' WHERE flatpak_app_id = 'com.github.rssguard'; 108 | UPDATE public.app SET in_store_since_date = '2018-03-06T09:09:36Z' WHERE flatpak_app_id = 'com.bladecoder.adventure-editor'; 109 | UPDATE public.app SET in_store_since_date = '2017-10-29T18:17:40Z' WHERE flatpak_app_id = 'org.musescore.MuseScore'; 110 | UPDATE public.app SET in_store_since_date = '2017-04-11T12:10:08Z' WHERE flatpak_app_id = 'com.uploadedlobster.peek'; 111 | UPDATE public.app SET in_store_since_date = '2018-01-18T13:45:28Z' WHERE flatpak_app_id = 'com.github.robertsanseries.ciano'; 112 | UPDATE public.app SET in_store_since_date = '2018-03-05T16:46:32Z' WHERE flatpak_app_id = 'com.github.gyunaev.spivak'; 113 | UPDATE public.app SET in_store_since_date = '2018-01-25T15:54:13Z' WHERE flatpak_app_id = 'io.github.jliljebl.Flowblade'; 114 | UPDATE public.app SET in_store_since_date = '2018-01-15T20:00:21Z' WHERE flatpak_app_id = 'org.gnome.meld'; 115 | UPDATE public.app SET in_store_since_date = '2018-03-06T08:54:46Z' WHERE flatpak_app_id = 'im.srain.Srain'; 116 | UPDATE public.app SET in_store_since_date = '2018-02-24T13:42:42Z' WHERE flatpak_app_id = 'org.gabmus.hydrapaper'; 117 | UPDATE public.app SET in_store_since_date = '2017-11-03T19:26:43Z' WHERE flatpak_app_id = 'com.github.babluboy.bookworm'; 118 | UPDATE public.app SET in_store_since_date = '2017-11-20T16:13:43Z' WHERE flatpak_app_id = 'com.remarkable.reMarkable'; 119 | UPDATE public.app SET in_store_since_date = '2018-03-08T11:18:12Z' WHERE flatpak_app_id = 'org.photoqt.PhotoQt'; 120 | UPDATE public.app SET in_store_since_date = '2018-01-12T12:58:53Z' WHERE flatpak_app_id = 'com.google.AndroidStudio'; 121 | UPDATE public.app SET in_store_since_date = '2017-11-09T03:41:23Z' WHERE flatpak_app_id = 'io.atom.Atom'; 122 | UPDATE public.app SET in_store_since_date = '2017-04-18T03:37:26Z' WHERE flatpak_app_id = 'com.teeworlds.Teeworlds'; 123 | UPDATE public.app SET in_store_since_date = '2018-01-11T14:49:53Z' WHERE flatpak_app_id = 'net.mediaarea.AVIMetaEdit'; 124 | UPDATE public.app SET in_store_since_date = '2017-08-24T10:41:22Z' WHERE flatpak_app_id = 'com.slack.Slack'; 125 | UPDATE public.app SET in_store_since_date = '2018-01-05T02:42:30Z' WHERE flatpak_app_id = 'io.exodus.Exodus'; 126 | UPDATE public.app SET in_store_since_date = '2017-04-11T16:14:58Z' WHERE flatpak_app_id = 'io.github.GnomeMpv'; 127 | UPDATE public.app SET in_store_since_date = '2018-03-06T09:04:20Z' WHERE flatpak_app_id = 'net.sf.nootka'; 128 | UPDATE public.app SET in_store_since_date = '2017-04-11T16:42:37Z' WHERE flatpak_app_id = 'org.DolphinEmu.dolphin-emu'; 129 | UPDATE public.app SET in_store_since_date = '2017-11-21T01:42:09Z' WHERE flatpak_app_id = 'com.visualstudio.code'; 130 | UPDATE public.app SET in_store_since_date = '2018-01-24T11:49:36Z' WHERE flatpak_app_id = 'us.zoom.Zoom'; 131 | UPDATE public.app SET in_store_since_date = '2017-09-19T17:27:10Z' WHERE flatpak_app_id = 'com.github.gkarsay.parlatype'; 132 | UPDATE public.app SET in_store_since_date = '2017-04-29T18:40:52Z' WHERE flatpak_app_id = 'com.github.needleandthread.vocal'; 133 | UPDATE public.app SET in_store_since_date = '2017-09-16T18:45:13Z' WHERE flatpak_app_id = 'de.haeckerfelix.gradio'; 134 | UPDATE public.app SET in_store_since_date = '2018-01-11T14:48:46Z' WHERE flatpak_app_id = 'net.mediaarea.DVAnalyzer'; 135 | UPDATE public.app SET in_store_since_date = '2018-02-22T11:58:57Z' WHERE flatpak_app_id = 'com.github.donadigo.appeditor'; 136 | UPDATE public.app SET in_store_since_date = '2017-12-04T20:28:28Z' WHERE flatpak_app_id = 'com.jetbrains.PyCharm-Community'; 137 | UPDATE public.app SET in_store_since_date = '2018-01-25T22:51:07Z' WHERE flatpak_app_id = 'net.mediaarea.MediaConch'; 138 | UPDATE public.app SET in_store_since_date = '2018-03-06T10:28:01Z' WHERE flatpak_app_id = 'com.endlessm.HatchPreviewer'; 139 | UPDATE public.app SET in_store_since_date = '2018-01-25T16:03:41Z' WHERE flatpak_app_id = 'net.mediaarea.MOVMetaEdit'; 140 | UPDATE public.app SET in_store_since_date = '2018-01-25T16:04:55Z' WHERE flatpak_app_id = 'net.mediaarea.QCTools'; 141 | UPDATE public.app SET in_store_since_date = '2017-04-11T16:13:44Z' WHERE flatpak_app_id = 'io.github.Hexchat'; 142 | UPDATE public.app SET in_store_since_date = '2017-06-18T08:42:20Z' WHERE flatpak_app_id = 'com.spotify.Client'; 143 | UPDATE public.app SET in_store_since_date = '2017-12-23T22:16:23Z' WHERE flatpak_app_id = 'com.github.paolostivanin.OTPClient'; 144 | UPDATE public.app SET in_store_since_date = '2017-12-18T14:56:04Z' WHERE flatpak_app_id = 'org.keepassxc.KeePassXC'; 145 | UPDATE public.app SET in_store_since_date = '2017-08-01T09:36:24Z' WHERE flatpak_app_id = 'org.gnome.iagno'; 146 | UPDATE public.app SET in_store_since_date = '2017-04-11T16:40:43Z' WHERE flatpak_app_id = 'org.ppsspp.PPSSPP'; 147 | UPDATE public.app SET in_store_since_date = '2017-08-01T10:44:04Z' WHERE flatpak_app_id = 'org.gnome.Glade'; 148 | UPDATE public.app SET in_store_since_date = '2017-11-08T06:21:40Z' WHERE flatpak_app_id = 'org.signal.Signal'; 149 | UPDATE public.app SET in_store_since_date = '2017-12-05T12:54:55Z' WHERE flatpak_app_id = 'com.github.birros.WebArchives'; 150 | UPDATE public.app SET in_store_since_date = '2017-11-30T08:10:47Z' WHERE flatpak_app_id = 'org.develz.Crawl'; 151 | UPDATE public.app SET in_store_since_date = '2017-06-18T08:02:43Z' WHERE flatpak_app_id = 'com.discordapp.Discord'; 152 | UPDATE public.app SET in_store_since_date = '2017-07-29T16:13:38Z' WHERE flatpak_app_id = 'org.gnome.Calendar'; 153 | UPDATE public.app SET in_store_since_date = '2017-09-08T22:15:59Z' WHERE flatpak_app_id = 'org.gnome.Photos'; 154 | UPDATE public.app SET in_store_since_date = '2017-12-04T16:17:51Z' WHERE flatpak_app_id = 'com.viewizard.AstroMenace'; 155 | UPDATE public.app SET in_store_since_date = '2017-11-07T17:24:26Z' WHERE flatpak_app_id = 'com.github.bajoja.indicator-kdeconnect'; 156 | UPDATE public.app SET in_store_since_date = '2018-01-11T18:45:04Z' WHERE flatpak_app_id = 'org.gnome.ghex'; 157 | UPDATE public.app SET in_store_since_date = '2017-04-12T17:32:08Z' WHERE flatpak_app_id = 'net.supertuxkart.SuperTuxKart'; 158 | UPDATE public.app SET in_store_since_date = '2017-09-15T16:59:39Z' WHERE flatpak_app_id = 'org.subsurface_divelog.Subsurface'; 159 | UPDATE public.app SET in_store_since_date = '2017-11-16T09:47:45Z' WHERE flatpak_app_id = 'org.fedoraproject.MediaWriter'; 160 | UPDATE public.app SET in_store_since_date = '2017-09-13T09:37:19Z' WHERE flatpak_app_id = 'com.github.JannikHv.Gydl'; 161 | UPDATE public.app SET in_store_since_date = '2017-09-05T15:48:06Z' WHERE flatpak_app_id = 'org.xiphos.Xiphos'; 162 | UPDATE public.app SET in_store_since_date = '2017-05-16T23:10:27Z' WHERE flatpak_app_id = 'ws.openarena.OpenArena'; 163 | UPDATE public.app SET in_store_since_date = '2017-04-26T06:11:11Z' WHERE flatpak_app_id = 'org.freeciv.Freeciv'; 164 | UPDATE public.app SET in_store_since_date = '2017-12-13T12:51:50Z' WHERE flatpak_app_id = 'org.gnome.Gnote'; 165 | UPDATE public.app SET in_store_since_date = '2017-11-03T19:22:52Z' WHERE flatpak_app_id = 'org.gnome.Games'; 166 | UPDATE public.app SET in_store_since_date = '2017-11-07T17:35:20Z' WHERE flatpak_app_id = 'com.bixense.PasswordCalculator'; 167 | UPDATE public.app SET in_store_since_date = '2017-09-26T09:01:51Z' WHERE flatpak_app_id = 'org.gnode.NixView'; 168 | UPDATE public.app SET in_store_since_date = '2017-07-06T09:47:02Z' WHERE flatpak_app_id = 'org.gnucash.GnuCash'; 169 | UPDATE public.app SET in_store_since_date = '2017-11-23T22:29:08Z' WHERE flatpak_app_id = 'work.openpaper.Paperwork'; 170 | UPDATE public.app SET in_store_since_date = '2018-01-05T11:39:07Z' WHERE flatpak_app_id = 'org.kde.kdenlive'; 171 | UPDATE public.app SET in_store_since_date = '2017-07-13T19:24:19Z' WHERE flatpak_app_id = 'io.elementary.code'; 172 | UPDATE public.app SET in_store_since_date = '2017-09-14T17:55:32Z' WHERE flatpak_app_id = 'ca._0ldsk00l.Nestopia'; 173 | UPDATE public.app SET in_store_since_date = '2017-08-09T07:23:26Z' WHERE flatpak_app_id = 'org.freedesktop.Bustle'; 174 | UPDATE public.app SET in_store_since_date = '2017-10-19T20:39:20Z' WHERE flatpak_app_id = 'io.github.Freedoom-Phase-2'; 175 | UPDATE public.app SET in_store_since_date = '2017-05-12T05:23:30Z' WHERE flatpak_app_id = 'net.minetest.Minetest'; 176 | UPDATE public.app SET in_store_since_date = '2018-01-26T21:29:42Z' WHERE flatpak_app_id = 'com.obsproject.Studio'; 177 | UPDATE public.app SET in_store_since_date = '2017-07-16T07:09:24Z' WHERE flatpak_app_id = 'ch.x29a.playitslowly'; 178 | UPDATE public.app SET in_store_since_date = '2018-01-10T15:54:05Z' WHERE flatpak_app_id = 'com.github.lainsce.notejot'; 179 | UPDATE public.app SET in_store_since_date = '2017-12-05T15:16:22Z' WHERE flatpak_app_id = 'org.freefilesync.FreeFileSync'; 180 | UPDATE public.app SET in_store_since_date = '2017-08-27T02:09:48Z' WHERE flatpak_app_id = 'com.github.wwmm.pulseeffects'; 181 | UPDATE public.app SET in_store_since_date = '2017-10-22T16:09:44Z' WHERE flatpak_app_id = 'io.github.cloose.CuteMarkEd'; 182 | UPDATE public.app SET in_store_since_date = '2017-11-07T14:43:27Z' WHERE flatpak_app_id = 'org.gnome.bijiben'; 183 | UPDATE public.app SET in_store_since_date = '2017-04-18T04:14:01Z' WHERE flatpak_app_id = 'com.play0ad.zeroad'; 184 | UPDATE public.app SET in_store_since_date = '2017-09-28T09:21:44Z' WHERE flatpak_app_id = 'com.github.philip_scott.spice-up'; 185 | UPDATE public.app SET in_store_since_date = '2018-01-25T15:57:11Z' WHERE flatpak_app_id = 'org.zotero.Zotero'; 186 | UPDATE public.app SET in_store_since_date = '2017-11-14T12:31:07Z' WHERE flatpak_app_id = 'org.gnome.Fractal'; 187 | UPDATE public.app SET in_store_since_date = '2017-12-07T17:25:38Z' WHERE flatpak_app_id = 'org.geany.Geany'; 188 | UPDATE public.app SET in_store_since_date = '2017-11-15T08:19:46Z' WHERE flatpak_app_id = 'com.github.quaternion'; 189 | UPDATE public.app SET in_store_since_date = '2017-04-11T14:21:47Z' WHERE flatpak_app_id = 'org.gnome.FeedReader'; 190 | UPDATE public.app SET in_store_since_date = '2017-04-18T03:47:49Z' WHERE flatpak_app_id = 'org.megaglest.MegaGlest'; 191 | UPDATE public.app SET in_store_since_date = '2017-04-26T06:23:04Z' WHERE flatpak_app_id = 'net.blockout.BlockOutII'; 192 | UPDATE public.app SET in_store_since_date = '2017-11-09T13:29:25Z' WHERE flatpak_app_id = 'org.gnome.Evince'; 193 | UPDATE public.app SET in_store_since_date = '2017-11-13T19:32:30Z' WHERE flatpak_app_id = 'org.libreoffice.LibreOffice'; 194 | UPDATE public.app SET in_store_since_date = '2017-04-12T14:24:40Z' WHERE flatpak_app_id = 'org.baedert.corebird'; 195 | UPDATE public.app SET in_store_since_date = '2018-02-28T14:13:15Z' WHERE flatpak_app_id = 'net.poedit.Poedit'; 196 | UPDATE public.app SET in_store_since_date = '2017-05-17T05:40:24Z' WHERE flatpak_app_id = 'net.sourceforge.ExtremeTuxRacer'; 197 | UPDATE public.app SET in_store_since_date = '2017-04-15T18:18:25Z' WHERE flatpak_app_id = 'org.tuxfamily.XMoto'; 198 | UPDATE public.app SET in_store_since_date = '2017-11-21T12:47:02Z' WHERE flatpak_app_id = 'net.mediaarea.MediaInfo'; 199 | UPDATE public.app SET in_store_since_date = '2017-11-09T14:51:21Z' WHERE flatpak_app_id = 'org.gnome.Todo'; 200 | UPDATE public.app SET in_store_since_date = '2017-07-21T00:41:01Z' WHERE flatpak_app_id = 'com.jagex.RuneScape'; 201 | UPDATE public.app SET in_store_since_date = '2017-12-17T20:19:25Z' WHERE flatpak_app_id = 'io.webtorrent.WebTorrent'; 202 | UPDATE public.app SET in_store_since_date = '2017-05-26T07:33:28Z' WHERE flatpak_app_id = 'net.sourceforge.TuxFootball'; 203 | UPDATE public.app SET in_store_since_date = '2017-06-23T19:49:04Z' WHERE flatpak_app_id = 'com.github.dahenson.agenda'; 204 | UPDATE public.app SET in_store_since_date = '2017-08-04T16:42:14Z' WHERE flatpak_app_id = 'com.scoutshonour.Digital'; 205 | UPDATE public.app SET in_store_since_date = '2018-02-09T15:07:26Z' WHERE flatpak_app_id = 'com.anydesk.Anydesk'; 206 | UPDATE public.app SET in_store_since_date = '2017-09-12T09:29:20Z' WHERE flatpak_app_id = 'com.github.ojubaorg.Othman'; 207 | UPDATE public.app SET in_store_since_date = '2017-11-09T14:40:21Z' WHERE flatpak_app_id = 'org.gnome.gitg'; 208 | UPDATE public.app SET in_store_since_date = '2017-04-15T18:24:04Z' WHERE flatpak_app_id = 'net.olofson.KoboDeluxe'; 209 | UPDATE public.app SET in_store_since_date = '2017-05-17T05:38:23Z' WHERE flatpak_app_id = 'org.mypaint.MyPaint'; 210 | UPDATE public.app SET in_store_since_date = '2018-02-03T22:01:57Z' WHERE flatpak_app_id = 'com.elsevier.MendeleyDesktop'; 211 | UPDATE public.app SET in_store_since_date = '2017-09-22T08:30:30Z' WHERE flatpak_app_id = 'org.nextcloud.Nextcloud'; 212 | UPDATE public.app SET in_store_since_date = '2017-10-17T08:33:41Z' WHERE flatpak_app_id = 'io.github.Cockatrice.cockatrice'; 213 | UPDATE public.app SET in_store_since_date = '2017-11-14T08:34:19Z' WHERE flatpak_app_id = 'org.gnome.gbrainy'; 214 | UPDATE public.app SET in_store_since_date = '2017-11-07T15:01:29Z' WHERE flatpak_app_id = 'org.gnome.Calculator'; 215 | UPDATE public.app SET in_store_since_date = '2018-02-19T09:30:10Z' WHERE flatpak_app_id = 'org.gnome.dfeet'; 216 | UPDATE public.app SET in_store_since_date = '2018-01-24T11:46:32Z' WHERE flatpak_app_id = 'io.github.FreeDM'; 217 | UPDATE public.app SET in_store_since_date = '2018-02-14T23:18:27Z' WHERE flatpak_app_id = 'org.mapeditor.Tiled'; 218 | UPDATE public.app SET in_store_since_date = '2017-10-18T05:26:01Z' WHERE flatpak_app_id = 'com.viber.Viber'; 219 | UPDATE public.app SET in_store_since_date = '2017-04-14T21:08:46Z' WHERE flatpak_app_id = 'io.thp.numptyphysics'; 220 | UPDATE public.app SET in_store_since_date = '2017-11-20T08:47:37Z' WHERE flatpak_app_id = 'net.sourceforge.projectM'; 221 | UPDATE public.app SET in_store_since_date = '2017-11-30T08:02:15Z' WHERE flatpak_app_id = 'com.grangerhub.Tremulous'; 222 | UPDATE public.app SET in_store_since_date = '2018-02-14T09:57:53Z' WHERE flatpak_app_id = 'com.github.bitseater.weather'; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.0__Initial_version.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | -- Dumped from database version 9.6.5 6 | -- Dumped by pg_dump version 9.6.6 7 | 8 | -- Started on 2018-01-17 07:25:58 CET 9 | 10 | SET statement_timeout = 0; 11 | -- SET lock_timeout = 0; 12 | -- SET idle_in_transaction_session_timeout = 0; 13 | SET client_encoding = 'UTF8'; 14 | SET standard_conforming_strings = on; 15 | SET check_function_bodies = false; 16 | SET client_min_messages = warning; 17 | -- SET row_security = off; 18 | 19 | -- 20 | -- TOC entry 1 (class 3079 OID 12390) 21 | -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 22 | -- 23 | 24 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; 25 | 26 | 27 | -- 28 | -- TOC entry 2170 (class 0 OID 0) 29 | -- Dependencies: 1 30 | -- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 31 | -- 32 | 33 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; 34 | 35 | 36 | SET search_path = public, pg_catalog; 37 | 38 | -- 39 | -- TOC entry 185 (class 1259 OID 16386) 40 | -- Name: app_app_id_seq; Type: SEQUENCE; Schema: public; Owner: linuxstore 41 | -- 42 | 43 | CREATE SEQUENCE app_app_id_seq 44 | START WITH 3052 45 | INCREMENT BY 1 46 | NO MINVALUE 47 | NO MAXVALUE 48 | CACHE 1; 49 | 50 | 51 | ALTER TABLE app_app_id_seq OWNER TO linuxstore; 52 | 53 | SET default_tablespace = ''; 54 | 55 | SET default_with_oids = false; 56 | 57 | -- 58 | -- TOC entry 188 (class 1259 OID 16399) 59 | -- Name: app; Type: TABLE; Schema: public; Owner: linuxstore 60 | -- 61 | 62 | CREATE TABLE app ( 63 | app_id integer DEFAULT nextval('app_app_id_seq'::regclass) NOT NULL, 64 | name character varying(128), 65 | summary character varying(1024), 66 | description character varying(4096), 67 | project_license character varying(1024), 68 | homepage_url character varying(2048), 69 | bugtracker_url character varying(2048), 70 | current_release_version character varying, 71 | flatpak_repo_id integer, 72 | flatpak_app_id character varying(128), 73 | first_release_date timestamp with time zone, 74 | first_release_version character varying(1024), 75 | current_release_date timestamp with time zone, 76 | rating double precision, 77 | rating_votes integer 78 | ); 79 | 80 | 81 | ALTER TABLE app OWNER TO linuxstore; 82 | 83 | -- 84 | -- TOC entry 189 (class 1259 OID 40970) 85 | -- Name: app_category; Type: TABLE; Schema: public; Owner: linuxstore 86 | -- 87 | 88 | CREATE TABLE app_category ( 89 | app_id integer NOT NULL, 90 | category_id integer NOT NULL 91 | ); 92 | 93 | 94 | ALTER TABLE app_category OWNER TO linuxstore; 95 | 96 | -- 97 | -- TOC entry 191 (class 1259 OID 41004) 98 | -- Name: category; Type: TABLE; Schema: public; Owner: linuxstore 99 | -- 100 | 101 | CREATE TABLE category ( 102 | category_id integer NOT NULL, 103 | name character varying(256) NOT NULL 104 | ); 105 | 106 | 107 | ALTER TABLE category OWNER TO linuxstore; 108 | 109 | -- 110 | -- TOC entry 190 (class 1259 OID 41002) 111 | -- Name: category_category_id_seq; Type: SEQUENCE; Schema: public; Owner: linuxstore 112 | -- 113 | 114 | CREATE SEQUENCE category_category_id_seq 115 | START WITH 1 116 | INCREMENT BY 1 117 | NO MINVALUE 118 | NO MAXVALUE 119 | CACHE 1; 120 | 121 | 122 | ALTER TABLE category_category_id_seq OWNER TO linuxstore; 123 | 124 | -- 125 | -- TOC entry 2171 (class 0 OID 0) 126 | -- Dependencies: 190 127 | -- Name: category_category_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: linuxstore 128 | -- 129 | 130 | ALTER SEQUENCE category_category_id_seq OWNED BY category.category_id; 131 | 132 | 133 | -- 134 | -- TOC entry 186 (class 1259 OID 16388) 135 | -- Name: flatpak_repo_flatpak_repo_id_seq; Type: SEQUENCE; Schema: public; Owner: linuxstore 136 | -- 137 | 138 | CREATE SEQUENCE flatpak_repo_flatpak_repo_id_seq 139 | START WITH 32 140 | INCREMENT BY 1 141 | NO MINVALUE 142 | NO MAXVALUE 143 | CACHE 1; 144 | 145 | 146 | ALTER TABLE flatpak_repo_flatpak_repo_id_seq OWNER TO linuxstore; 147 | 148 | -- 149 | -- TOC entry 187 (class 1259 OID 16390) 150 | -- Name: flatpak_repo; Type: TABLE; Schema: public; Owner: linuxstore 151 | -- 152 | 153 | CREATE TABLE flatpak_repo ( 154 | flatpak_repo_id integer DEFAULT nextval('flatpak_repo_flatpak_repo_id_seq'::regclass) NOT NULL, 155 | name character varying(128), 156 | description character varying(1024), 157 | url character varying(2048), 158 | homepage_url character varying(2048), 159 | default_branch character varying(128), 160 | gpgkey character varying(16384), 161 | download_flatpakrepo_url character varying(2048) 162 | ); 163 | 164 | 165 | ALTER TABLE flatpak_repo OWNER TO linuxstore; 166 | 167 | -- 168 | -- TOC entry 193 (class 1259 OID 49156) 169 | -- Name: screenshot; Type: TABLE; Schema: public; Owner: linuxstore 170 | -- 171 | 172 | CREATE TABLE screenshot ( 173 | screenshot_id integer NOT NULL, 174 | thumb_url character varying(2048), 175 | img_mobile_url character varying(2048), 176 | img_desktop_url character varying(2048), 177 | app_id integer NOT NULL 178 | ); 179 | 180 | 181 | ALTER TABLE screenshot OWNER TO linuxstore; 182 | 183 | -- 184 | -- TOC entry 192 (class 1259 OID 49154) 185 | -- Name: screenshot_screenshot_id_seq; Type: SEQUENCE; Schema: public; Owner: linuxstore 186 | -- 187 | 188 | CREATE SEQUENCE screenshot_screenshot_id_seq 189 | START WITH 1 190 | INCREMENT BY 1 191 | NO MINVALUE 192 | NO MAXVALUE 193 | CACHE 1; 194 | 195 | 196 | ALTER TABLE screenshot_screenshot_id_seq OWNER TO linuxstore; 197 | 198 | -- 199 | -- TOC entry 2029 (class 2604 OID 41007) 200 | -- Name: category category_id; Type: DEFAULT; Schema: public; Owner: linuxstore 201 | -- 202 | 203 | ALTER TABLE ONLY category ALTER COLUMN category_id SET DEFAULT nextval('category_category_id_seq'::regclass); 204 | 205 | 206 | -- 207 | -- TOC entry 2033 (class 2606 OID 16407) 208 | -- Name: app app_pkey; Type: CONSTRAINT; Schema: public; Owner: linuxstore 209 | -- 210 | 211 | ALTER TABLE ONLY app 212 | ADD CONSTRAINT app_pkey PRIMARY KEY (app_id); 213 | 214 | 215 | -- 216 | -- TOC entry 2037 (class 2606 OID 41017) 217 | -- Name: category category_name_unique; Type: CONSTRAINT; Schema: public; Owner: linuxstore 218 | -- 219 | 220 | ALTER TABLE ONLY category 221 | ADD CONSTRAINT category_name_unique UNIQUE (name); 222 | 223 | 224 | -- 225 | -- TOC entry 2039 (class 2606 OID 41009) 226 | -- Name: category category_pkey; Type: CONSTRAINT; Schema: public; Owner: linuxstore 227 | -- 228 | 229 | ALTER TABLE ONLY category 230 | ADD CONSTRAINT category_pkey PRIMARY KEY (category_id); 231 | 232 | 233 | -- 234 | -- TOC entry 2031 (class 2606 OID 16398) 235 | -- Name: flatpak_repo flatpak_repo_pkey; Type: CONSTRAINT; Schema: public; Owner: linuxstore 236 | -- 237 | 238 | ALTER TABLE ONLY flatpak_repo 239 | ADD CONSTRAINT flatpak_repo_pkey PRIMARY KEY (flatpak_repo_id); 240 | 241 | 242 | -- 243 | -- TOC entry 2042 (class 2606 OID 49163) 244 | -- Name: screenshot screenshot_pkey; Type: CONSTRAINT; Schema: public; Owner: linuxstore 245 | -- 246 | 247 | ALTER TABLE ONLY screenshot 248 | ADD CONSTRAINT screenshot_pkey PRIMARY KEY (screenshot_id); 249 | 250 | 251 | -- 252 | -- TOC entry 2034 (class 1259 OID 40984) 253 | -- Name: fki_app_category_app_id_fkey; Type: INDEX; Schema: public; Owner: linuxstore 254 | -- 255 | 256 | CREATE INDEX fki_app_category_app_id_fkey ON app_category USING btree (app_id); 257 | 258 | 259 | -- 260 | -- TOC entry 2035 (class 1259 OID 41015) 261 | -- Name: fki_app_category_category_id_fkey; Type: INDEX; Schema: public; Owner: linuxstore 262 | -- 263 | 264 | CREATE INDEX fki_app_category_category_id_fkey ON app_category USING btree (category_id); 265 | 266 | 267 | -- 268 | -- TOC entry 2040 (class 1259 OID 49169) 269 | -- Name: fki_screenshot_app_id_fkey; Type: INDEX; Schema: public; Owner: linuxstore 270 | -- 271 | 272 | CREATE INDEX fki_screenshot_app_id_fkey ON screenshot USING btree (app_id); 273 | 274 | 275 | -- 276 | -- TOC entry 2044 (class 2606 OID 40985) 277 | -- Name: app_category app_category_app_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: linuxstore 278 | -- 279 | 280 | ALTER TABLE ONLY app_category 281 | ADD CONSTRAINT app_category_app_id_fkey FOREIGN KEY (app_id) REFERENCES app(app_id); 282 | 283 | 284 | -- 285 | -- TOC entry 2045 (class 2606 OID 41010) 286 | -- Name: app_category app_category_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: linuxstore 287 | -- 288 | 289 | ALTER TABLE ONLY app_category 290 | ADD CONSTRAINT app_category_category_id_fkey FOREIGN KEY (category_id) REFERENCES category(category_id); 291 | 292 | 293 | -- 294 | -- TOC entry 2043 (class 2606 OID 16408) 295 | -- Name: app app_flatpak_repo_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: linuxstore 296 | -- 297 | 298 | ALTER TABLE ONLY app 299 | ADD CONSTRAINT app_flatpak_repo_id_fkey FOREIGN KEY (flatpak_repo_id) REFERENCES flatpak_repo(flatpak_repo_id); 300 | 301 | 302 | -- 303 | -- TOC entry 2046 (class 2606 OID 49164) 304 | -- Name: screenshot screenshot_app_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: linuxstore 305 | -- 306 | 307 | ALTER TABLE ONLY screenshot 308 | ADD CONSTRAINT screenshot_app_id_fkey FOREIGN KEY (app_id) REFERENCES app(app_id); 309 | 310 | 311 | -- Completed on 2018-01-17 07:25:58 CET 312 | 313 | -- 314 | -- PostgreSQL database dump complete 315 | -- 316 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.1__flatpakrepo_add_current_ostree_commit.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.flatpak_repo 2 | ADD COLUMN current_ostree_commit character varying(128); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.2__app_add_icon_xxx_url.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.app 2 | ADD COLUMN icon_desktop_url character varying(2048); 3 | 4 | ALTER TABLE public.app 5 | ADD COLUMN icon_mobile_url character varying(2048); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.3__app_rename_in_store_since_date.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.app 2 | RENAME first_release_date TO in_store_since_date; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.4__app_rename_current_release_description.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.app 2 | RENAME first_release_version TO current_release_description; 3 | 4 | ALTER TABLE public.app 5 | ALTER COLUMN current_release_description TYPE character varying(4096); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.5__app_add_appdata_xxx_url.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.app 2 | ADD COLUMN help_url character varying(2048); 3 | 4 | ALTER TABLE public.app 5 | ADD COLUMN donation_url character varying(2048); 6 | 7 | ALTER TABLE public.app 8 | ADD COLUMN translate_url character varying(2048); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.6__app_add_appdata_developer_name.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE public.app 2 | ADD COLUMN developer_name character varying(1024); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1/V1.7__apprelease.sql: -------------------------------------------------------------------------------- 1 | CREATE SEQUENCE apprelease_apprelease_id_seq 2 | INCREMENT BY 1 3 | MINVALUE 1 4 | MAXVALUE 9223372036854775807 5 | START 1 6 | CACHE 1 7 | NO CYCLE; 8 | 9 | ALTER TABLE apprelease_apprelease_id_seq OWNER TO linuxstore; 10 | 11 | CREATE TABLE app_release ( 12 | apprelease_id integer DEFAULT nextval('apprelease_apprelease_id_seq'::regclass) NOT NULL, 13 | appdata_release_version varchar(1024) NULL, 14 | appdata_release_version_updated bool NOT NULL, 15 | arch varchar(32) NULL, 16 | downloads int4 NOT NULL, 17 | installs int4 NOT NULL, 18 | ostree_commit_date timestamp NULL, 19 | ostree_commit_hash varchar(512) NULL, 20 | ostree_commit_hash_parent varchar(512) NULL, 21 | ostree_commit_subject varchar(2048) NULL, 22 | ostree_commit_date_next timestamp NULL, 23 | updates int4 NOT NULL, 24 | app_id int4 NULL, 25 | download_size varchar(512) NULL, 26 | installed_size varchar(512) NULL, 27 | runtime varchar(512) NULL, 28 | sdk varchar(512) NULL, 29 | is_end_of_life bool NOT NULL, 30 | end_of_life_info varchar(512) NULL, 31 | end_of_life_rebase varchar(512) NULL, 32 | metadata varchar(8192) NULL, 33 | CONSTRAINT app_release_pkey PRIMARY KEY (apprelease_id), 34 | CONSTRAINT app_release FOREIGN KEY (app_id) REFERENCES app(app_id) 35 | ); 36 | 37 | 38 | ALTER TABLE app_release OWNER TO linuxstore; -------------------------------------------------------------------------------- /src/test/java/org/flathub/ApiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.flathub; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.ActiveProfiles; 7 | import org.springframework.test.context.junit4.SpringRunner; 8 | 9 | @RunWith(SpringRunner.class) 10 | @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) 11 | @ActiveProfiles(value = "DEV") 12 | public class ApiApplicationTests { 13 | 14 | @SuppressWarnings("EmptyMethod") 15 | @Test 16 | public void contextLoads() { 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/org/flathub/api/service/ApiServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | import org.flathub.api.model.App; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.context.ActiveProfiles; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | import java.util.List; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | /** 16 | * Created by jorge on 17/12/17. 17 | */ 18 | 19 | @RunWith(SpringRunner.class) 20 | @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) 21 | @ActiveProfiles(value = "DEV") 22 | public class ApiServiceImplTest { 23 | 24 | 25 | @Autowired 26 | ApiService service; 27 | 28 | @Test 29 | public void findAllApps() throws Exception { 30 | 31 | //Given 32 | 33 | //When 34 | List list = service.findAllApps(); 35 | 36 | //Then 37 | assertThat(list).isNotEmpty(); 38 | 39 | } 40 | 41 | @Test 42 | public void findAllAppsByCategoryName() throws Exception { 43 | } 44 | 45 | @Test 46 | public void findAppByFlatpakAppId() throws Exception { 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/test/java/org/flathub/api/service/LocalFlatpakInstallationServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package org.flathub.api.service; 2 | 3 | import org.flathub.api.model.Arch; 4 | import org.flathub.api.model.FlatpakRefRemoteInfo; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.ActiveProfiles; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import java.time.LocalDateTime; 13 | import java.time.format.DateTimeFormatter; 14 | import java.util.List; 15 | import java.util.Optional; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | @SuppressWarnings("OptionalGetWithoutIsPresent") 20 | @RunWith(SpringRunner.class) 21 | @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) 22 | @ActiveProfiles(value = "DEV") 23 | public class LocalFlatpakInstallationServiceImplTest { 24 | 25 | @Autowired 26 | private 27 | LocalFlatpakInstallationServiceImpl service; 28 | 29 | @SuppressWarnings("OptionalGetWithoutIsPresent") 30 | @Test 31 | public void when_getQuickBasicRemoteInfoByArchAndId_Gedit_x86_64_Expect_SameBasicInfoAsGetRemoteInfoByArchAndId() { 32 | 33 | //Given 34 | 35 | //When 36 | Optional basicInfo = service.getQuickBasicRemoteInfoByRemoteAndArchAndId("flathub", Arch.X86_64, "org.gnome.gedit"); 37 | Optional completeInfo = service.getRemoteInfoByRemoteAndArchAndId("flathub", Arch.X86_64, "org.gnome.gedit"); 38 | 39 | //Then 40 | assertThat(basicInfo.isPresent()).isTrue(); 41 | assertThat(completeInfo.isPresent()).isTrue(); 42 | 43 | assertThat(basicInfo.get().getRef()).isEqualToIgnoringCase(completeInfo.get().getRef()); 44 | assertThat(basicInfo.get().getId()).isEqualToIgnoringCase(completeInfo.get().getId()); 45 | assertThat(basicInfo.get().getArch()).isEqualToIgnoringCase(completeInfo.get().getArch()); 46 | assertThat(basicInfo.get().getBranch()).isEqualToIgnoringCase(completeInfo.get().getBranch()); 47 | assertThat(basicInfo.get().getCollection()).isNullOrEmpty(); 48 | assertThat(basicInfo.get().getDate()).isNull(); 49 | assertThat(basicInfo.get().getSubject()).isNullOrEmpty(); 50 | assertThat(basicInfo.get().getCommit()).isNullOrEmpty(); 51 | assertThat(basicInfo.get().getShortCommit()).isEqualToIgnoringCase(completeInfo.get().getShortCommit()); 52 | assertThat(basicInfo.get().getParent()).isNullOrEmpty(); 53 | assertThat(basicInfo.get().getDownloadSize()).isEqualToIgnoringCase(completeInfo.get().getDownloadSize()); 54 | assertThat(basicInfo.get().getInstalledSize()).isEqualToIgnoringCase(completeInfo.get().getInstalledSize()); 55 | assertThat(basicInfo.get().getRuntime()).isNullOrEmpty(); 56 | assertThat(basicInfo.get().getSdk()).isNullOrEmpty(); 57 | assertThat(basicInfo.get().isEndOfLife()).isFalse(); 58 | assertThat(basicInfo.get().getEndOfLife()).isNullOrEmpty(); 59 | assertThat(basicInfo.get().getEndOfLifeRebase()).isNullOrEmpty(); 60 | 61 | 62 | } 63 | 64 | @Test 65 | public void when_getQuickBasicRemoteInfoOfEoldRef_Expect_CorrectEoldInfo() { 66 | 67 | //Given 68 | 69 | //When 70 | Optional info = service.getQuickBasicRemoteInfoByRemoteAndArchAndId("flathub", Arch.X86_64, "com.github.bitseater.weather"); 71 | 72 | //Then 73 | assertThat(info.isPresent()).isTrue(); 74 | assertThat(info.isPresent()); 75 | assertThat(info.get().getRef()).isEqualToIgnoringCase("app/com.github.bitseater.weather/x86_64/stable"); 76 | assertThat(info.get().getId()).isEqualToIgnoringCase("com.github.bitseater.weather"); 77 | assertThat(info.get().getArch()).isEqualToIgnoringCase(Arch.X86_64.toString()); 78 | assertThat(info.get().getBranch()).isEqualToIgnoringCase("stable"); 79 | assertThat(info.get().getCollection()).isNullOrEmpty(); 80 | assertThat(info.get().getDate()).isNull(); 81 | assertThat(info.get().getSubject()).isNullOrEmpty(); 82 | assertThat(info.get().getCommit()).isNullOrEmpty(); 83 | assertThat(info.get().getShortCommit()).isEqualToIgnoringCase("aaa4a1810027"); 84 | assertThat(info.get().getParent()).isNullOrEmpty(); 85 | assertThat(info.get().getDownloadSize()).isEqualToIgnoringCase("1,2 MB"); 86 | assertThat(info.get().getInstalledSize()).isEqualToIgnoringCase("6,1 MB"); 87 | assertThat(info.get().getRuntime()).isNullOrEmpty(); 88 | assertThat(info.get().getSdk()).isNullOrEmpty(); 89 | 90 | assertThat(info.get().isEndOfLife()).isTrue(); 91 | assertThat(info.get().getEndOfLife()).isEqualToIgnoringCase("This application has been renamed to com.gitlab.bitseater.meteo"); 92 | assertThat(info.get().getEndOfLifeRebase()).isNullOrEmpty(); 93 | 94 | } 95 | 96 | @Test 97 | public void when_getRemoteInfoOfEoldRef_Expect_CorrectEoldInfo() { 98 | 99 | //Given 100 | 101 | //When 102 | Optional info = service.getRemoteInfoByRemoteAndArchAndId("flathub", Arch.X86_64, "com.github.bitseater.weather"); 103 | 104 | //Then 105 | assertThat(info.isPresent()).isTrue(); 106 | assertThat(info.isPresent()); 107 | assertThat(info.get().getRef()).isEqualToIgnoringCase("app/com.github.bitseater.weather/x86_64/stable"); 108 | assertThat(info.get().getId()).isEqualToIgnoringCase("com.github.bitseater.weather"); 109 | assertThat(info.get().getArch()).isEqualToIgnoringCase(Arch.X86_64.toString()); 110 | assertThat(info.get().getBranch()).isEqualToIgnoringCase("stable"); 111 | assertThat(info.get().getCollection()).isEqualToIgnoringCase("org.flathub.Stable"); 112 | 113 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z"); 114 | assertThat(info.get().getDate()).isEqualToComparingFieldByField(LocalDateTime.parse("2018-12-12 14:34:14 +0000", formatter)); 115 | 116 | 117 | assertThat(info.get().getSubject()).isEqualToIgnoringCase("Update eol message (2f82d9d8)"); 118 | assertThat(info.get().getCommit()).isEqualToIgnoringCase("aaa4a1810027de1b32e9e077288622de7824a6f25622359889a8192e21d86597"); 119 | assertThat(info.get().getShortCommit()).isEqualToIgnoringCase("aaa4a1810027"); 120 | assertThat(info.get().getParent()).isEqualToIgnoringCase("17b37809fe86abb44a84588829790059e74c9321ebfdf0f90225b57bcaa8c002"); 121 | assertThat(info.get().getDownloadSize()).isEqualToIgnoringCase("1,2 MB"); 122 | assertThat(info.get().getInstalledSize()).isEqualToIgnoringCase("6,1 MB"); 123 | assertThat(info.get().getRuntime()).isEqualToIgnoringCase("org.gnome.Platform/x86_64/3.26"); 124 | assertThat(info.get().getSdk()).isEqualToIgnoringCase("org.gnome.Sdk/x86_64/3.26"); 125 | assertThat(info.get().isEndOfLife()).isTrue(); 126 | assertThat(info.get().getEndOfLife()).isEqualToIgnoringCase("This application has been renamed to com.gitlab.bitseater.meteo"); 127 | assertThat(info.get().getEndOfLifeRebase()).isNullOrEmpty(); 128 | } 129 | 130 | 131 | 132 | 133 | 134 | @Test 135 | public void when_getRemoteMetatataByRemoteAndArchAndId_Expect_CorrectMetadataObtained() { 136 | 137 | //Given 138 | 139 | //When 140 | 141 | Optional metadata = service.getRemoteMetatataByRemoteAndArchAndId("flathub", Arch.X86_64, "com.github.bitseater.weather"); 142 | 143 | 144 | //Then 145 | assertThat(metadata.isPresent()); 146 | assertThat(metadata.get().equalsIgnoreCase("[Application]\n" + 147 | "name=com.github.bitseater.weather\n" + 148 | "runtime=org.gnome.Platform/x86_64/3.26\n" + 149 | "sdk=org.gnome.Sdk/x86_64/3.26\n" + 150 | "command=com.github.bitseater.weather\n" + 151 | "\n" + 152 | "[Context]\n" + 153 | "shared=network;ipc;\n" + 154 | "sockets=x11;wayland;\n" + 155 | "devices=dri;\n" + 156 | "filesystems=xdg-run/dconf;~/.config/dconf:ro;\n" + 157 | "\n" + 158 | "[Session Bus Policy]\n" + 159 | "org.kde.StatusNotifierWatcher=talk\n" + 160 | "ca.desrt.dconf=talk\n" + 161 | "\n" + 162 | "[System Bus Policy]\n" + 163 | "org.freedesktop.GeoClue2=talk\n" + 164 | "\n" + 165 | "[Environment]\n" + 166 | "DCONF_USER_CONFIG_DIR=.config/dconf\n" + 167 | "\n" + 168 | "[Extension com.github.bitseater.weather.Locale]\n" + 169 | "directory=share/runtime/locale\n" + 170 | "autodelete=true\n" + 171 | "locale-subset=true\n" + 172 | "\n" + 173 | "[Build]\n" + 174 | "built-extensions=com.github.bitseater.weather.Locale;com.github.bitseater.weather.Sources;\n")); 175 | 176 | 177 | 178 | } 179 | 180 | 181 | @Test 182 | public void when_getQuickBasicRemoteInfoOfAllEoldRefs_Expect_CorrectEoldInfo() { 183 | 184 | //Given 185 | 186 | //When 187 | 188 | List remoteInfoList = service.getAllQuickBasicRemoteInfoByRemote("flathub"); 189 | 190 | //Then 191 | assertThat(remoteInfoList).isNotEmpty(); 192 | 193 | for(FlatpakRefRemoteInfo info : remoteInfoList){ 194 | if(info.isEndOfLife()){ 195 | assertThat(info.getEndOfLife()).isNotEmpty(); 196 | } 197 | } 198 | 199 | } 200 | 201 | 202 | @Test 203 | public void when_getRemoteInfoByArchAndId_Gedit_x86_64_Expect_InfoObtained() { 204 | 205 | //Given 206 | 207 | //When 208 | Optional info = service.getRemoteInfoByRemoteAndArchAndId("flathub", Arch.X86_64, "org.gnome.gedit"); 209 | 210 | //Then 211 | assertThat(info.isPresent()); 212 | assertThat(info.get().getRef()).isEqualToIgnoringCase("app/org.gnome.gedit/x86_64/stable"); 213 | assertThat(info.get().getId()).isEqualToIgnoringCase("org.gnome.gedit"); 214 | assertThat(info.get().getArch()).isEqualToIgnoringCase(Arch.X86_64.toString()); 215 | assertThat(info.get().getBranch()).isEqualToIgnoringCase("stable"); 216 | assertThat(info.get().getCollection()).isEqualToIgnoringCase("org.flathub.Stable"); 217 | assertThat(info.get().getDate()).isInstanceOf(LocalDateTime.class); 218 | assertThat(info.get().getDate()).isGreaterThan(LocalDateTime.of(2018, 1, 1, 0, 0,0)); 219 | assertThat(info.get().getSubject()).isNotEmpty(); 220 | assertThat(info.get().getCommit()).isNotEmpty(); 221 | assertThat(info.get().getParent()).isNotEmpty(); 222 | assertThat(info.get().getDownloadSize()).isNotEmpty(); 223 | assertThat(info.get().getInstalledSize()).isNotEmpty(); 224 | assertThat(info.get().getRuntime()).startsWith("org.gnome.Platform/x86_64/"); 225 | assertThat(info.get().getSdk()).startsWith("org.gnome.Sdk/x86_64/"); 226 | assertThat(info.get().isEndOfLife()).isFalse(); 227 | assertThat(info.get().getEndOfLife()).isNullOrEmpty(); 228 | assertThat(info.get().getEndOfLifeRebase()).isNullOrEmpty(); 229 | } 230 | 231 | @Test 232 | public void when_getRemoteInfoByArchAndId_Gedit_i386_Expect_InfoObtained() { 233 | 234 | //Given 235 | 236 | //When 237 | Optional info = service.getRemoteInfoByRemoteAndArchAndId("flathub", Arch.I386, "org.gnome.gedit"); 238 | 239 | //Then 240 | assertThat(info.isPresent()); 241 | assertThat(info.get().getRef()).isEqualToIgnoringCase("app/org.gnome.gedit/i386/stable"); 242 | assertThat(info.get().getId()).isEqualToIgnoringCase("org.gnome.gedit"); 243 | assertThat(info.get().getArch()).isEqualToIgnoringCase(Arch.I386.toString()); 244 | assertThat(info.get().getBranch()).isEqualToIgnoringCase("stable"); 245 | assertThat(info.get().getCollection()).isEqualToIgnoringCase("org.flathub.Stable"); 246 | assertThat(info.get().getDate()).isInstanceOf(LocalDateTime.class); 247 | assertThat(info.get().getDate()).isGreaterThan(LocalDateTime.of(2018, 1, 1, 0, 0,0)); 248 | assertThat(info.get().getSubject()).isNotEmpty(); 249 | assertThat(info.get().getCommit()).isNotEmpty(); 250 | assertThat(info.get().getParent()).isNotEmpty(); 251 | assertThat(info.get().getDownloadSize()).isNotEmpty(); 252 | assertThat(info.get().getInstalledSize()).isNotEmpty(); 253 | assertThat(info.get().getRuntime()).startsWith("org.gnome.Platform/i386/"); 254 | assertThat(info.get().getSdk()).startsWith("org.gnome.Sdk/i386/"); 255 | assertThat(info.get().isEndOfLife()).isFalse(); 256 | assertThat(info.get().getEndOfLife()).isNullOrEmpty(); 257 | assertThat(info.get().getEndOfLifeRebase()).isNullOrEmpty(); 258 | } 259 | 260 | @Test 261 | public void when_getRemoteInfoByArchAndId_Gedit_arm_Expect_InfoObtained() { 262 | 263 | //Given 264 | 265 | //When 266 | Optional info = service.getRemoteInfoByRemoteAndArchAndId("flathub", Arch.ARM, "org.gnome.gedit"); 267 | 268 | //Then 269 | assertThat(info.isPresent()); 270 | assertThat(info.get().getRef()).isEqualToIgnoringCase("app/org.gnome.gedit/arm/stable"); 271 | assertThat(info.get().getId()).isEqualToIgnoringCase("org.gnome.gedit"); 272 | assertThat(info.get().getArch()).isEqualToIgnoringCase(Arch.ARM.toString()); 273 | assertThat(info.get().getBranch()).isEqualToIgnoringCase("stable"); 274 | assertThat(info.get().getCollection()).isEqualToIgnoringCase("org.flathub.Stable"); 275 | assertThat(info.get().getDate()).isInstanceOf(LocalDateTime.class); 276 | assertThat(info.get().getDate()).isGreaterThan(LocalDateTime.of(2018, 1, 1, 0, 0,0)); 277 | assertThat(info.get().getSubject()).isNotEmpty(); 278 | assertThat(info.get().getCommit()).isNotEmpty(); 279 | assertThat(info.get().getParent()).isNotEmpty(); 280 | assertThat(info.get().getDownloadSize()).isNotEmpty(); 281 | assertThat(info.get().getInstalledSize()).isNotEmpty(); 282 | assertThat(info.get().getRuntime()).startsWith("org.gnome.Platform/arm/"); 283 | assertThat(info.get().getSdk()).startsWith("org.gnome.Sdk/arm/"); 284 | assertThat(info.get().isEndOfLife()).isFalse(); 285 | assertThat(info.get().getEndOfLife()).isNullOrEmpty(); 286 | assertThat(info.get().getEndOfLifeRebase()).isNullOrEmpty(); 287 | } 288 | 289 | @Test 290 | public void when_getRemoteInfoByArchAndId_Gedit_aarch64_Expect_InfoObtained() { 291 | 292 | //Given 293 | 294 | //When 295 | Optional info = service.getRemoteInfoByRemoteAndArchAndId("flathub", Arch.AARCH64, "org.gnome.gedit"); 296 | 297 | //Then 298 | assertThat(info.isPresent()); 299 | assertThat(info.get().getRef()).isEqualToIgnoringCase("app/org.gnome.gedit/aarch64/stable"); 300 | assertThat(info.get().getId()).isEqualToIgnoringCase("org.gnome.gedit"); 301 | assertThat(info.get().getArch()).isEqualToIgnoringCase(Arch.AARCH64.toString()); 302 | assertThat(info.get().getBranch()).isEqualToIgnoringCase("stable"); 303 | assertThat(info.get().getCollection()).isEqualToIgnoringCase("org.flathub.Stable"); 304 | assertThat(info.get().getDate()).isInstanceOf(LocalDateTime.class); 305 | assertThat(info.get().getDate()).isGreaterThan(LocalDateTime.of(2018, 1, 1, 0, 0,0)); 306 | assertThat(info.get().getSubject()).isNotEmpty(); 307 | assertThat(info.get().getCommit()).isNotEmpty(); 308 | assertThat(info.get().getParent()).isNotEmpty(); 309 | assertThat(info.get().getDownloadSize()).isNotEmpty(); 310 | assertThat(info.get().getInstalledSize()).isNotEmpty(); 311 | assertThat(info.get().getRuntime()).startsWith("org.gnome.Platform/aarch64/"); 312 | assertThat(info.get().getSdk()).startsWith("org.gnome.Sdk/aarch64/"); 313 | assertThat(info.get().isEndOfLife()).isFalse(); 314 | assertThat(info.get().getEndOfLife()).isNullOrEmpty(); 315 | assertThat(info.get().getEndOfLifeRebase()).isNullOrEmpty(); 316 | assertThat(info.get().isEndOfLife()).isFalse(); 317 | assertThat(info.get().getEndOfLife()).isNullOrEmpty(); 318 | assertThat(info.get().getEndOfLifeRebase()).isNullOrEmpty(); 319 | } 320 | 321 | 322 | @Test 323 | public void when_getRemoteInfoByArchAndId_AppNotInRepo_Expect_InfoNotObtained() { 324 | 325 | //Given 326 | 327 | //When 328 | Optional info = service.getRemoteInfoByRemoteAndArchAndId("flathub", Arch.X86_64, "org.gnome.NOTPRESENT"); 329 | 330 | //Then 331 | assertThat(!info.isPresent()); 332 | 333 | } 334 | 335 | 336 | } 337 | -------------------------------------------------------------------------------- /src/test/resources/application-TEST.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | datasource: 4 | url: jdbc:hsqldb:mem:flathub 5 | username: sa 6 | driver-class-name: org.hsqldb.jdbcDriver 7 | #data: classpath*:data.sql 8 | jpa: 9 | database: HSQL 10 | # Create BD tables automatically 11 | hibernate: 12 | ddl-auto: create 13 | show-sql: true 14 | # mail: 15 | # host: smtp.gmail.com 16 | # port: 587 17 | # username: 18 | # password: 19 | # smtp.auth: true 20 | # smtp.starttls.enable: true 21 | 22 | 23 | hibernate: 24 | showSql: true 25 | dialect: org.hibernate.dialect.org.hibernate.dialect.HSQLDialect 26 | 27 | flathub: 28 | appstream-extractor-info: /var/lib/appstream-extractor/appstream-extractor.info 29 | flatpakref: 30 | server-path: /var/www/main-store/apps/ 31 | url: http://localhost:80/main-store/apps/ 32 | icons: 33 | server-path: /var/www/main-store/icons/ 34 | url: http://localhost:80/main-store/icons/ 35 | -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Set default profile 2 | spring.profiles.active=TEST 3 | # Set default error page 4 | server.error.whitelabel.enabled=true 5 | # Add flathubapi tot the url 6 | # localhost:8080/flathubapi 7 | # server.context-path:/flathubapi 8 | # Make Jackson serialize dates in ISO format (2016-04-29T11:46:59.670+0000) 9 | # instead of timestamp (1461930419670) 10 | spring.jackson.serialization.write_dates_as_timestamps=false 11 | ##################################################################### 12 | # Logs configuration: 13 | # +info a http://blog.netgloo.com/2014/12/11/logging-in-spring-boot/ 14 | # 15 | # Level for loggers on classes inside the root package "flathub" 16 | # (and its sub-packages) 17 | # Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF 18 | logging.level.org.flathub=DEBUG 19 | # Specify the level for spring boot and hibernate's loggers 20 | logging.level.org.springframework.boot.autoconfigure.security=INFO 21 | logging.level.org.springframework.web=INFO 22 | logging.level.org.hibernate=ERROR 23 | # Log file location (in addition to the console) 24 | logging.file=linux-store-backend.log 25 | --------------------------------------------------------------------------------