diff --git a/.gitignore b/.gitignore index cc9b55cdf..985328742 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Eclipse .project .classpath +.factorypath .settings/ bin/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile index d8e4c379e..eeaeb21df 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,11 @@ dev: ./mvnw compile quarkus:dev +#make debug +.PHONY: debug +debug: + ./mvnw compile quarkus:dev -Ddebug -Dsuspend + #make clean .PHONY: clean clean: @@ -11,7 +16,7 @@ clean: #make native .PHONY: native native: - ./mvnw package -Pnative + ./mvnw package -Pnative -DskipTests #make ext-add ID=kotlin .PHONY: ext-add @@ -22,8 +27,3 @@ ext-add: .PHONY: ext-list ext-list: ./mvnw quarkus:list-extensions - -#make ext-add VERSION=0.22.0 -.PHONY: update-ext -update-ext: - yarn --cwd ./extensions start $(VERSION) diff --git a/README.md b/README.md index bd2beb4e0..b1313e53e 100644 --- a/README.md +++ b/README.md @@ -29,29 +29,16 @@ You can check deployed commit hash on: https://stage.code.quarkus.io/api/config # To update the Quarkus version (after a new Quarkus release) -1. [Update the extension list](#update-the-code-quarkus-extension-list) with the new Quarkus version -2. Edit the `pom.xml` with the new Quarkus version `x.y.z` -3. Provide a PR, merge, and [promote to production](#promote-to-production) - -# Update the Code Quarkus extension list - -`x.y.z` is the Quarkus version. - +1. Edit the `pom.xml` with the new **Quarkus** & **compatible Quarkus Platform version**: ``` -$ yarn --cwd ./extensions && yarn --cwd ./extensions start x.y.z -``` - -It will automatically generate a new `extensions.json` to commit. + + x.y.z -# Include a new extension + + a.b.c +``` +2. Provide a PR, merge, and [promote to production](#promote-to-production) -1. The extension must be available in a Quarkus release: - https://github.com/quarkusio/quarkus/blob/x.y.z/devtools/common/src/main/filtered/extensions.json -2. The extension must be added to the website extension list (with metadata): - https://github.com/quarkusio/quarkusio.github.io/blob/develop/_data/extensions.yaml - > Updating the website will soon not be required anymore, you can follow progress here: https://github.com/quarkusio/code.quarkus.io/issues/40 -3. [Update the extension list](#update-the-code-quarkus-extension-list) - # API Documentation diff --git a/extensions/.gitignore b/extensions/.gitignore deleted file mode 100644 index 3881573ea..000000000 --- a/extensions/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -.history/ - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# ide -*.iws -*.iml -.idea/ \ No newline at end of file diff --git a/extensions/generate-extensions.ts b/extensions/generate-extensions.ts deleted file mode 100644 index d43c59ebe..000000000 --- a/extensions/generate-extensions.ts +++ /dev/null @@ -1,69 +0,0 @@ -import axios from 'axios'; -import * as yaml from 'yaml'; -import { promises} from 'fs'; - -interface Extension { - id: string; - name: string; - labels: string[]; - description?: string; - shortName?: string; - category: string; - order: number; -} - -function getId(e) { - return e.groupId + ':' + e.artifactId; -} - -const version = process.argv.length > 2 ? process.argv[2] : 'master'; - -function flatten(arr) { - return arr.reduce(function (flat, toFlatten) { - return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); - }, []); -} - -async function generate() { - console.log(`Quarkus version: ${version}`); - const extWebsiteResp = await axios.get('https://raw.githubusercontent.com/quarkusio/quarkusio.github.io/develop/_data/extensions.yaml'); - const extLibResp = await axios.get(`https://raw.githubusercontent.com/quarkusio/quarkus/${version}/devtools/platform-descriptor-legacy/src/main/filtered/extensions.json`); - - const extWebsite = yaml.parse(extWebsiteResp.data); - - const fExtWebsite = flatten(extWebsite.categories.map(c => { - return c.extensions.map(e => ({ - ...e, - category: c.category, - categoryId: c['cat-id'] - })); - })); - const extLibById = new Map(extLibResp.data.map(f => [getId(f), f])); - const out = fExtWebsite.map((eWebsite, i) => { - const id = getId(eWebsite); - const eLib = extLibById.get(id); - if(!eLib) { - console.warn('Extension missing in lib ' + id); - return undefined; - } - if(!eWebsite.description) { - console.warn('Description missing for ' + id); - } - if(!eLib.shortName) { - console.warn('Shortname missing for ' + id); - } - return { - id, - name: eWebsite.name, - labels: eWebsite.labels, - description: eWebsite.description, - shortName: eLib.shortName, - category: eWebsite.category, - order: i, - } - }).filter(e => !!e); - - await promises.writeFile('../src/main/resources/quarkus/extensions.json', JSON.stringify(out)); -} - -generate().catch(e => console.error(e)); \ No newline at end of file diff --git a/extensions/package.json b/extensions/package.json deleted file mode 100644 index 80422a6e1..000000000 --- a/extensions/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "quarkus-extensions-converter", - "version": "1.0.0", - "private": true, - "dependencies": { - "axios": "0.19.0", - "ts-node": "8.3.0", - "typescript": "3.5.2", - "yaml": "1.6.0" - }, - "scripts": { - "start": "ts-node generate-extensions.ts" - }, - "devDependencies": { - "@types/node": "12.6.9", - "@types/yaml": "1.0.2" - } -} diff --git a/extensions/yarn.lock b/extensions/yarn.lock deleted file mode 100644 index cd26bb3bd..000000000 --- a/extensions/yarn.lock +++ /dev/null @@ -1,118 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/runtime@^7.4.5": - version "7.5.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.2.tgz#98f584f4d03be5d8142c77107ffaedee4d5956f1" - integrity sha512-9M29wrrP7//JBGX70+IrDuD1w4iOYhUGpJNMQJVNAXue+cFeFlMTqBECouIziXPUphlgrfjcfiEpGX4t0WGK4g== - dependencies: - regenerator-runtime "^0.13.2" - -"@types/node@12.6.9": - version "12.6.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.9.tgz#ffeee23afdc19ab16e979338e7b536fdebbbaeaf" - integrity sha512-+YB9FtyxXGyD54p8rXwWaN1EWEyar5L58GlGWgtH2I9rGmLGBQcw63+0jw+ujqVavNuO47S1ByAjm9zdHMnskw== - -"@types/yaml@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/yaml/-/yaml-1.0.2.tgz#bba080d64714c6ef3eaa023e235dacd2cfa3c938" - integrity sha512-rS1VJFjyGKNHk8H97COnPIK+oeLnc0J9G0ES63o/Ky+WlJCeaFGiGCTGhV/GEVKua7ZWIV1JIDopYUwrfvTo7A== - -arg@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0" - integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg== - -axios@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" - integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== - dependencies: - follow-redirects "1.5.10" - is-buffer "^2.0.2" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -debug@=3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -diff@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" - integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== - -follow-redirects@1.5.10: - version "1.5.10" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" - integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== - dependencies: - debug "=3.1.0" - -is-buffer@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" - integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== - -make-error@^1.1.1: - version "1.3.5" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" - integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -regenerator-runtime@^0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" - integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== - -source-map-support@^0.5.6: - version "0.5.12" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" - integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -ts-node@8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.3.0.tgz#e4059618411371924a1fb5f3b125915f324efb57" - integrity sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ== - dependencies: - arg "^4.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.6" - yn "^3.0.0" - -typescript@3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c" - integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA== - -yaml@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.6.0.tgz#d8a985cfb26086dd73f91c637f6e6bc909fddd3c" - integrity sha512-iZfse3lwrJRoSlfs/9KQ9iIXxs9++RvBFVzAqbbBiFT+giYtyanevreF9r61ZTbGMgWQBxAua3FzJiniiJXWWw== - dependencies: - "@babel/runtime" "^7.4.5" - -yn@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.0.tgz#fcbe2db63610361afcc5eb9e0ac91e976d046114" - integrity sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg== diff --git a/pom.xml b/pom.xml index ab1609db8..9851cdc83 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,12 @@ 1.8 1.7.6 1.19 - 0.26.1 + + + 1.0.0.CR1 + + + 1.0.0.CR1 @@ -55,7 +60,12 @@ io.quarkus - quarkus-platform-descriptor-legacy + quarkus-platform-descriptor-json + ${version.quarkus} + + + io.quarkus + quarkus-platform-descriptor-resolver-json ${version.quarkus} @@ -134,12 +144,19 @@ ${version.frontend-maven-plugin} - install node and yarn + install node and yarn - pre pre-clean install-node-and-yarn + + install node and yarn + generate-resources + + install-node-and-yarn + + yarn install diff --git a/reflection-config.json b/reflection-config.json index 197ec9c4f..08eba5050 100644 --- a/reflection-config.json +++ b/reflection-config.json @@ -7,14 +7,5 @@ "allPublicMethods" : true, "allDeclaredFields" : true, "allPublicFields" : true - }, - { - "name" : "io.quarkus.platform.descriptor.loader.legacy.QuarkusLegacyPlatformDescriptorLoader", - "allDeclaredConstructors" : true, - "allPublicConstructors" : true, - "allDeclaredMethods" : true, - "allPublicMethods" : true, - "allDeclaredFields" : true, - "allPublicFields" : true } ] \ No newline at end of file diff --git a/resources-config.json b/resources-config.json index 3e6d5be50..4887d8700 100644 --- a/resources-config.json +++ b/resources-config.json @@ -15,9 +15,6 @@ { "pattern": "creator/.*$" }, - { - "pattern": "quarkus/extensions\\.json$" - }, { "pattern": "quarkus\\.properties" }, diff --git a/src/main/docker/Dockerfile.jvm.multistage b/src/main/docker/Dockerfile.jvm.multistage index d6f7a641c..84caa1e33 100644 --- a/src/main/docker/Dockerfile.jvm.multistage +++ b/src/main/docker/Dockerfile.jvm.multistage @@ -1,5 +1,5 @@ ## Stage 1 : build with maven builder image with native capabilities -FROM quay.io/quarkus/centos-quarkus-maven:19.2.0.1 AS build +FROM quay.io/quarkus/centos-quarkus-maven:19.2.1 AS build COPY .git /usr/src/app/.git COPY src /usr/src/app/src COPY pom.xml /usr/src/app diff --git a/src/main/docker/Dockerfile.native.multistage b/src/main/docker/Dockerfile.native.multistage index be3e61fd4..6edbe972f 100644 --- a/src/main/docker/Dockerfile.native.multistage +++ b/src/main/docker/Dockerfile.native.multistage @@ -1,5 +1,5 @@ ## Stage 1 : build with maven builder image with native capabilities -FROM quay.io/quarkus/centos-quarkus-maven:19.2.0.1 AS build +FROM quay.io/quarkus/centos-quarkus-maven:19.2.1 AS build COPY .git /usr/src/app/.git COPY src /usr/src/app/src COPY pom.xml /usr/src/app diff --git a/src/main/frontend/package.json b/src/main/frontend/package.json index 11097191d..77a25ce26 100644 --- a/src/main/frontend/package.json +++ b/src/main/frontend/package.json @@ -3,8 +3,8 @@ "version": "0.1.0", "private": true, "dependencies": { - "@patternfly/react-core": "3.104.0", - "@patternfly/react-icons": "3.14.2", + "@patternfly/react-core": "3.112.3", + "@patternfly/react-icons": "3.14.16", "@sentry/browser": "5.0.8", "@types/classnames": "2.2.7", "@types/jest": "24.0.17", diff --git a/src/main/frontend/public/bg-form.jpg b/src/main/frontend/public/bg-form.jpg new file mode 100644 index 000000000..a664ecb40 Binary files /dev/null and b/src/main/frontend/public/bg-form.jpg differ diff --git a/src/main/frontend/public/bg-header.jpg b/src/main/frontend/public/bg-header.jpg new file mode 100644 index 000000000..3a3150ac3 Binary files /dev/null and b/src/main/frontend/public/bg-header.jpg differ diff --git a/src/main/frontend/public/index.html b/src/main/frontend/public/index.html index 8f68e96f5..d5050bcdb 100755 --- a/src/main/frontend/public/index.html +++ b/src/main/frontend/public/index.html @@ -9,36 +9,35 @@ Quarkus - Start coding with code.quarkus.io - - - - - + + + + + + +
- Start coding with + + Start coding with code.quarkus.io
diff --git a/src/main/frontend/public/quarkus_icon_rgb_reverse.svg b/src/main/frontend/public/quarkus-icon-splash.svg similarity index 100% rename from src/main/frontend/public/quarkus_icon_rgb_reverse.svg rename to src/main/frontend/public/quarkus-icon-splash.svg diff --git a/src/main/frontend/public/quarkus-logo.svg b/src/main/frontend/public/quarkus-logo.svg new file mode 100644 index 000000000..ace6ab3ff --- /dev/null +++ b/src/main/frontend/public/quarkus-logo.svg @@ -0,0 +1 @@ +quarkus_logo_horizontal_1color_1280px_reverse \ No newline at end of file diff --git a/src/main/frontend/public/quarkus_background.jpg b/src/main/frontend/public/quarkus_background.jpg deleted file mode 100644 index 43aa9b366..000000000 Binary files a/src/main/frontend/public/quarkus_background.jpg and /dev/null differ diff --git a/src/main/frontend/public/quarkus_background_header.jpg b/src/main/frontend/public/quarkus_background_header.jpg deleted file mode 100644 index 3b78782a5..000000000 Binary files a/src/main/frontend/public/quarkus_background_header.jpg and /dev/null differ diff --git a/src/main/frontend/public/quarkus_background_main.jpg b/src/main/frontend/public/quarkus_background_main.jpg deleted file mode 100644 index 59eaadb31..000000000 Binary files a/src/main/frontend/public/quarkus_background_main.jpg and /dev/null differ diff --git a/src/main/frontend/public/quarkus_background_mobile.jpg b/src/main/frontend/public/quarkus_background_mobile.jpg deleted file mode 100644 index 8f135d873..000000000 Binary files a/src/main/frontend/public/quarkus_background_mobile.jpg and /dev/null differ diff --git a/src/main/frontend/public/quarkus_logo_horizontal_rgb_reverse.svg b/src/main/frontend/public/quarkus_logo_horizontal_rgb_reverse.svg deleted file mode 100644 index 1734560dd..000000000 --- a/src/main/frontend/public/quarkus_logo_horizontal_rgb_reverse.svg +++ /dev/null @@ -1 +0,0 @@ -quarkus_logo_horizontal_rgb_1280px_reverse \ No newline at end of file diff --git a/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap b/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap index 76c45e59c..154525d86 100644 --- a/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap +++ b/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Let user Generate default application 1`] = `"/api/download?g=org.acme&a=code-with-quarkus&v=1.0.0-SNAPSHOT&c=org.acme.ExampleResource&"`; +exports[`Let user Generate default application 1`] = `"/api/download?g=org.acme&a=code-with-quarkus&v=1.0.0-SNAPSHOT&b=MAVEN&c=org.acme.ExampleResource&"`; -exports[`Let user customize an Application and Generate it 1`] = `"/api/download?g=io.test.group&a=custom-test-app&v=1.0.0-TEST&c=io.test.pack.ExampleResource&e=io.quarkus%3Aquarkus-arc&e=io.quarkus%3Aquarkus-resteasy&e=io.quarkus%3Aquarkus-resteasy-jackson"`; +exports[`Let user customize an Application and Generate it 1`] = `"/api/download?g=io.test.group&a=custom-test-app&v=1.0.0-TEST&b=MAVEN&c=io.test.pack.ExampleResource&e=io.quarkus%3Aquarkus-arc&e=io.quarkus%3Aquarkus-resteasy&e=io.quarkus%3Aquarkus-resteasy-jackson"`; diff --git a/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx b/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx index dfda67803..915449a4b 100644 --- a/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx +++ b/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx @@ -7,7 +7,7 @@ jest.mock('../backend-api', () => ({ { "id": "io.quarkus:quarkus-arc", "name": "ArC", - "labels": [ + "keywords": [ "arc", "cdi", "dependency-injection", @@ -21,7 +21,7 @@ jest.mock('../backend-api', () => ({ { "id": "io.quarkus:quarkus-resteasy", "name": "RESTEasy JAX-RS", - "labels": [ + "keywords": [ "resteasy", "jaxrs", "web", @@ -35,7 +35,7 @@ jest.mock('../backend-api', () => ({ { "id": "io.quarkus:quarkus-resteasy-jsonb", "name": "RESTEasy JSON-B", - "labels": [ + "keywords": [ "resteasy-jsonb", "jaxrs-json", "resteasy-json", @@ -51,7 +51,7 @@ jest.mock('../backend-api', () => ({ { "id": "io.quarkus:quarkus-resteasy-jackson", "name": "RESTEasy Jackson", - "labels": [ + "keywords": [ "resteasy-jackson", "jaxrs-json", "resteasy-json", diff --git a/src/main/frontend/src/code-quarkus/backend-api.ts b/src/main/frontend/src/code-quarkus/backend-api.ts index 0c1280c9f..b19ebbdf9 100644 --- a/src/main/frontend/src/code-quarkus/backend-api.ts +++ b/src/main/frontend/src/code-quarkus/backend-api.ts @@ -6,7 +6,7 @@ export async function fetchExtensions() { return await data.json(); } catch(e) { throw new Error("Failed to load Quarkus extension list"); - } + } } export async function fetchConfig() { @@ -17,5 +17,5 @@ export async function fetchConfig() { return { environment: 'dev' } - } + } } \ No newline at end of file diff --git a/src/main/frontend/src/code-quarkus/code-quarkus.scss b/src/main/frontend/src/code-quarkus/code-quarkus.scss index 6d6abd22f..e3458a9cf 100755 --- a/src/main/frontend/src/code-quarkus/code-quarkus.scss +++ b/src/main/frontend/src/code-quarkus/code-quarkus.scss @@ -1,8 +1,9 @@ @import url(https://fonts.googleapis.com/css?family=PT+Mono); @import url(https://fonts.googleapis.com/css?family=Open+Sans); +@import "config.scss"; body { - background: #000; + background: $background; font-family: "Open Sans", Regular; } @@ -31,7 +32,7 @@ body { } .pf-c-backdrop__open { - background: #000; + background: $background; } .pf-c-backdrop { @@ -46,43 +47,43 @@ body { .pf-c-alert__icon { background-color: transparent; - color: #74AFED; + color: $alert; } .pf-c-alert__title { font-weight: bold; - color: white; + color: $main; } .pf-c-alert__description { - color: white; + color: $main; } .pf-c-alert__action button.pf-c-button{ &:hover { - color: white; + color: $main; } } } .next-steps-modal { - background-color: #000000; - color: #ffffff; - border: 4px solid #ff004a; + background-color: $background; + color: $main; + border: 4px solid $highlight; .pf-c-content { - color: #ffffff; + color: $main; .pf-c-button.pf-m-link { padding: 0; - color: #ff004a; + color: $highlight; } } .pf-c-title { - color: #4695eb; + color: $highlight; font-weight: bold; } & > .pf-c-button { - background-color: #ff004a; + background-color: $highlight; height: 25px; width: 25px; line-height: 25px; @@ -90,10 +91,10 @@ body { padding: 0; border-radius: 0; svg { - color: #000000; + color: $background; } &:hover { - background-color: rgba(255, 0, 72, 0.548); + background-color: rgba(70, 149, 235, 0.548); } } @@ -110,7 +111,7 @@ body { } .download-button { - background-color: #ff004a; + background-color: $highlight; border-radius: 0; text-transform: uppercase; width: 200px; @@ -121,15 +122,15 @@ body { .pf-c-modal-box__footer { button.pf-c-button { - background-color: white; - color: #ff004a; + background-color: $main; + color: $highlight; border-radius: 0; - border: 1px solid white; + border: 1px solid $main; } button.pf-c-button:hover { - border: 1px solid #ff004a; - color: #ff004a; + border: 1px solid $highlight; + color: $highlight; } button.pf-c-button::after { diff --git a/src/main/frontend/src/code-quarkus/code-quarkus.tsx b/src/main/frontend/src/code-quarkus/code-quarkus.tsx index 8e01ecd90..58f162d95 100644 --- a/src/main/frontend/src/code-quarkus/code-quarkus.tsx +++ b/src/main/frontend/src/code-quarkus/code-quarkus.tsx @@ -30,6 +30,7 @@ export interface QuarkusProject { version: string; name?: string; packageName?: string; + buildTool: string; } extensions: string[]; } @@ -40,6 +41,7 @@ async function generateProject(project: QuarkusProject): Promise<{ downloadLink: ...(project.metadata.groupId && { g: project.metadata.groupId }), ...(project.metadata.artifactId && { a: project.metadata.artifactId }), ...(project.metadata.version && { v: project.metadata.version }), + ...(project.metadata.buildTool && {b: project.metadata.buildTool}), ...(packageName && { c: `${packageName}.ExampleResource` }), ...(project.extensions && { e: project.extensions }), } @@ -54,6 +56,7 @@ const DEFAULT_PROJECT = { groupId: 'org.acme', artifactId: 'code-with-quarkus', version: '1.0.0-SNAPSHOT', + buildTool: 'MAVEN' }, extensions: [], }; @@ -92,9 +95,9 @@ export function CodeQuarkus(props: LaunchFlowProps) {
- + {!run.error && run.status === Status.DOWNLOADED - && ()} + && ()}
); diff --git a/src/main/frontend/src/code-quarkus/config.scss b/src/main/frontend/src/code-quarkus/config.scss new file mode 100644 index 000000000..e5e5f367c --- /dev/null +++ b/src/main/frontend/src/code-quarkus/config.scss @@ -0,0 +1,10 @@ + +$background: #000; +$primary: #71aeef; +$secondary: #ff004a; + +$readonly: #707070; +$alert: #74afed; +$highlight: #4695eb; +$main: white; + diff --git a/src/main/frontend/src/code-quarkus/copy-to-clipboard.tsx b/src/main/frontend/src/code-quarkus/copy-to-clipboard.tsx index c7ab0e008..50bfc0c44 100644 --- a/src/main/frontend/src/code-quarkus/copy-to-clipboard.tsx +++ b/src/main/frontend/src/code-quarkus/copy-to-clipboard.tsx @@ -1,32 +1,42 @@ -import React, { useState } from "react"; +import React, { useState, MouseEvent } from "react"; import copy from 'copy-to-clipboard'; -import {ClipboardCheckIcon, ClipboardIcon} from "@patternfly/react-icons"; +import { ClipboardCheckIcon, ClipboardIcon } from "@patternfly/react-icons"; import { useAnalytics } from '../core'; +import { Tooltip } from '@patternfly/react-core'; -export function CopyToClipboard(props: { eventId?: string, content: string }) { +type TooltipPosition = 'auto' | 'top' | 'bottom' | 'left' | 'right'; + +export function CopyToClipboard(props: { eventId?: string, content: string, tooltipPosition?: TooltipPosition, zIndex?: number }) { const [active, setActive] = useState(false); const [copied, setCopied] = useState(false); + const [copiedText, setCopiedText] = useState(false); const analytics = useAnalytics(); - const copyToClipboard = () => { + const copyToClipboard = (e: MouseEvent) => { + e.stopPropagation(); copy(props.content); - if(props.eventId && !copied) { + if (props.eventId && !copied) { analytics && analytics.event('Copy-To-Clipboard', props.eventId, props.content); } setCopied(true); - setTimeout(() => setCopied(false), 1000); + setCopiedText(true); + setTimeout(() => setCopiedText(false), 2000); + setTimeout(() => setCopied(false), 1500); } + const tooltip = copiedText ?

Successfuly copied to clipboard!

: Copy to clipboard:
{props.content}
; return ( -
setActive(true)} - onMouseLeave={() => setActive(false)} - onClick={copyToClipboard} - className="copy-to-clipboard" - style={{cursor: 'pointer'}} - > - {active || copied ? : } -
+ +
setActive(true)} + onMouseLeave={() => setActive(false)} + onClick={copyToClipboard} + className="copy-to-clipboard" + style={{ cursor: 'pointer' }} + > + {active || copied ? : } +
+
) } diff --git a/src/main/frontend/src/code-quarkus/extensions-loader.tsx b/src/main/frontend/src/code-quarkus/extensions-loader.tsx index 1280aa54e..0be749948 100644 --- a/src/main/frontend/src/code-quarkus/extensions-loader.tsx +++ b/src/main/frontend/src/code-quarkus/extensions-loader.tsx @@ -6,11 +6,13 @@ import { fetchExtensions } from './backend-api'; interface Extension { id: string; name: string; - labels: string[]; + keywords: string[]; description?: string; shortName?: string; category: string; - order: number, + default: boolean; + order: number; + status: string; } export function ExtensionsLoader(props: { name: string, children: (entries: ExtensionEntry[]) => any }) { diff --git a/src/main/frontend/src/code-quarkus/form.scss b/src/main/frontend/src/code-quarkus/form.scss index b5f1b05d6..c92e6fe56 100644 --- a/src/main/frontend/src/code-quarkus/form.scss +++ b/src/main/frontend/src/code-quarkus/form.scss @@ -1,16 +1,17 @@ +@import "config.scss"; + .code-quarkus-form { padding-bottom: 30px; @media screen and (max-width: 900px) { margin: 0 10px; } - + .form-header-sticky-container { position: sticky; top: 0; - height: 200px; - background-image: url("/quarkus_background_main.jpg"); - background-size: 100% 200px; + background-image: url("/bg-form.jpg"); + background-size: 100% 220px; background-repeat: no-repeat; z-index: 110; .form-header { @@ -23,8 +24,8 @@ } } - .title h3{ - color: white; + .title h3 { + color: $main; font-size: 32px; font-weight: bold; margin-bottom: 20px; @@ -32,7 +33,7 @@ .project-info { flex-basis: 75%; - height: 170px; + height: 200px; margin-top: 20px; .base-settings { margin: 0 20px 0 0; @@ -42,19 +43,22 @@ margin: 0 0 0 20px; padding: 0; } - + @media screen and (max-width: 1200px) { - .extended-settings, .toggle-button { display: none; } + .extended-settings, + .toggle-button { + display: none; + } } .pf-c-button { - color: white; + color: $main; font-size: 14px; text-transform: uppercase; text-align: left; width: 233px; svg { - color: #FF004A; + color: $main; } } } @@ -70,7 +74,7 @@ display: none; } } - + .control-container { margin-top: 20px; position: sticky; @@ -89,28 +93,29 @@ .generate-project { flex: 0 0 280px; - align-self:center; - height: 180px; + align-self: center; + height: 200px; display: flex; flex-direction: row; justify-content: flex-end; button.generate-button { - background-color: #FF004A; + background-color: $highlight; border-radius: 0; + box-shadow: 0px 2px 2px #00000040; width: 280px; height: 36px; align-self: flex-end; text-align: center; - border: 1px solid #FF004A; - color: white; + border: 1px solid $highlight; + color: $main; &:hover { - border: 1px solid #ffffff; + border: 1px solid $main; } - &.pf-m-disabled { - background-color: rgba(255, 0, 72, 0.342); - border: 1px solid rgba(255, 0, 72, 0.342); + &:disabled { + background-color: rgba(90, 149, 235, 0.342); + border: 1px solid rgba(90, 149, 235, 0.342); color: rgba(255, 255, 255, 0.384); } } @@ -120,33 +125,70 @@ .pf-c-form__group { .pf-c-form__label { display: inline-block; - width: 100px; - color: #9AFF2D; + width: 115px; + color: $primary; font-size: 14px; font-weight: bold; text-transform: capitalize; } - - input.pf-c-form-control { + + input.pf-c-form-control, + select.pf-c-form-control { border: none; - margin-left: -100px; - padding-left: 100px; - border-bottom: 1px solid #9AFF2D; + margin-left: -115px; + padding-left: 115px; + border-bottom: 1px solid $primary; font-size: 14px; width: 100%; background-color: transparent; - color: white; + color: $main; outline: none; font-family: "PT Mono", Regular; padding-bottom: 4px; padding-right: 30px; } - + + &.quarkus-version { + + .pf-c-form__label { + color: $readonly; + } + input.pf-c-form-control { + cursor: not-allowed; + padding-right: 82px; + margin-right: -82px; + color: #bdbcbc; + border-bottom: 1px solid $readonly; + + &:focus { + border-bottom: 1px solid $readonly; + } + } + &:after { + content: "READ-ONLY"; + float: right; + color: $readonly; + font-size: 11px; + padding: 2px; + margin-top: 7px; + margin-right: 5px; + border: 1px solid $readonly; + } + } + + select.pf-c-form-control { + --pf-c-form-control__select--Background: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='%2371aeef' d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'/%3E%3C/svg%3E") + calc(100% - 0.5rem) center/0.875rem no-repeat; + } + + select.pf-c-form-control > option { + color: $background; + } + input.pf-c-form-control:focus { outline: none; - border-bottom: 1px solid #4695EB; + border-bottom: 1px solid $highlight; } } - } -} \ No newline at end of file +} diff --git a/src/main/frontend/src/code-quarkus/form.tsx b/src/main/frontend/src/code-quarkus/form.tsx index 16c1efbc4..495a6d86c 100644 --- a/src/main/frontend/src/code-quarkus/form.tsx +++ b/src/main/frontend/src/code-quarkus/form.tsx @@ -10,6 +10,7 @@ import { InfoPicker } from './pickers/info-picker'; interface CodeQuarkusFormProps { project: QuarkusProject; setProject: React.Dispatch>; + quarkusVersion: string; onSave: () => void; } @@ -22,7 +23,7 @@ export function CodeQuarkusForm(props: CodeQuarkusFormProps) { }; const setExtensions = (value: { extensions: string[] }) => setProject((prev) => ({ ...prev, extensions: value.extensions })); const save = () => { - if(isMetadataValid) { + if (isMetadataValid) { props.onSave(); } }; @@ -36,7 +37,7 @@ export function CodeQuarkusForm(props: CodeQuarkusFormProps) {

Application Info

- +
@@ -54,6 +55,7 @@ export function CodeQuarkusForm(props: CodeQuarkusFormProps) { value={{ extensions: props.project.extensions }} onChange={setExtensions} placeholder="RESTEasy, Hibernate ORM, Web..." + buildTool={props.project.metadata.buildTool} /> )} diff --git a/src/main/frontend/src/code-quarkus/header.scss b/src/main/frontend/src/code-quarkus/header.scss index 3854a3b49..61c3e3336 100644 --- a/src/main/frontend/src/code-quarkus/header.scss +++ b/src/main/frontend/src/code-quarkus/header.scss @@ -1,45 +1,34 @@ +@import "config.scss"; + .header { - height: 132px; - background-image: url("/quarkus_background_header.jpg"); - background-size: 100% 132px; - background-repeat: no-repeat; + height: 70px; + background-image: url("/bg-header.jpg"); + background-size: 100% 70px; + background-repeat: repeat-x; .header-content { - display: flex; + grid-column: span 12; align-items: center; - height: 132px; - margin: 0 auto; - + width: 100%; margin: 0 auto; + .nav-container { + max-width: 1000px; + margin: 0 auto; + float: right; + } .brand { - flex-grow: 1; - .brand-image { - background-image: url("/quarkus_logo_horizontal_rgb_reverse.svg"); - background-repeat: no-repeat; - background-origin: content-box; - background-position: center; - width: 277px; - height: 47px; - - @media screen and (max-width: 1200px) { - width: 200px; - height: 47px; - } - - @media screen and (max-width: 900px) { - width: 100%; - } - + display: inline-block; + margin-top: 1.2rem; + width: 13rem; + @media screen and (max-width: 900px) { + margin-left: 20px; } } - .nav { - display: flex; - flex-grow: 1; - justify-content: flex-end; + .nav-list { + height: 70px; text-transform: uppercase; - font-size: 1.2rem; white-space: nowrap; @media screen and (max-width: 1200px) { @@ -50,14 +39,39 @@ display: none; } + li { + float: left; + position: relative; + margin: 0; + } + a { - color: #fff; + display: inline-block; + color: $main; padding: 0 10px; + text-decoration: none; + line-height: 70px; + height: 55px; + font-size: 1rem; + } + a:hover { + border-bottom: 3px solid $main; } - a:hover, a.active { + margin-left: 3px; text-decoration: none; - border-bottom: 3px solid #fff; + color: $main; + border: 2px solid $main; + border: 2px solid $highlight; + height: 37px; + line-height: 37px; + margin-top: 15px; + background-color: $highlight; + border-bottom: 0; + transition: background .2s ease-in 0s; + &:hover { + color: $main; + } } } } diff --git a/src/main/frontend/src/code-quarkus/header.tsx b/src/main/frontend/src/code-quarkus/header.tsx index 6413c14f9..6d1227f0e 100644 --- a/src/main/frontend/src/code-quarkus/header.tsx +++ b/src/main/frontend/src/code-quarkus/header.tsx @@ -6,15 +6,19 @@ export function Header() {
); diff --git a/src/main/frontend/src/code-quarkus/next-steps.tsx b/src/main/frontend/src/code-quarkus/next-steps.tsx index fb398995a..007c9836d 100644 --- a/src/main/frontend/src/code-quarkus/next-steps.tsx +++ b/src/main/frontend/src/code-quarkus/next-steps.tsx @@ -6,6 +6,7 @@ import { CopyToClipboard } from './copy-to-clipboard'; interface NextStepsProps { downloadLink?: string; onClose?(reset?: boolean): void; + buildTool: string } export function NextSteps(props: NextStepsProps) { @@ -35,7 +36,14 @@ export function NextSteps(props: NextStepsProps) {

What's next?

Unzip the project and start playing with Quarkus by running: - $ ./mvnw compile quarkus:dev + + {props.buildTool === 'MAVEN' && + $ ./mvnw compile quarkus:dev + } + + {props.buildTool === 'GRADLE' && + $ ./gradlew quarkusDev + } Follow the guides for your next steps!
diff --git a/src/main/frontend/src/code-quarkus/pickers/__tests__/__snapshots__/extensions-picker.spec.tsx.snap b/src/main/frontend/src/code-quarkus/pickers/__tests__/__snapshots__/extensions-picker.spec.tsx.snap index 1ef5b62a7..2fd6e7e36 100644 --- a/src/main/frontend/src/code-quarkus/pickers/__tests__/__snapshots__/extensions-picker.spec.tsx.snap +++ b/src/main/frontend/src/code-quarkus/pickers/__tests__/__snapshots__/extensions-picker.spec.tsx.snap @@ -38,8 +38,9 @@ exports[` renders the ExtensionsPicker correctly 1`] = ` aria-invalid="false" aria-label="Search extensions" class="pf-c-form-control search-extensions-input" + id="extension-search" placeholder="" - type="text" + type="search" value="" />
@@ -167,11 +168,12 @@ exports[` renders the ExtensionsPicker correctly 1`] = ` Core
renders the ExtensionsPicker correctly 1`] = `
- ArC + + ArC +
renders the ExtensionsPicker correctly 1`] = `
renders the ExtensionsPicker correctly 1`] = ` Cloud
renders the ExtensionsPicker correctly 1`] = `
- A CDI in name test + + A CDI in name test +
renders the ExtensionsPicker correctly 1`] = `
renders the ExtensionsPicker correctly 1`] = ` Integration
renders the ExtensionsPicker correctly 1`] = `
- Camel Netty4 test HTTP + + Camel Netty4 test HTTP +
renders the ExtensionsPicker correctly 1`] = `
select values and save 1`] = ` aria-invalid="false" aria-label="Search extensions" class="pf-c-form-control search-extensions-input" + id="extension-search" placeholder="" - type="text" + type="search" value="netty" />
@@ -551,11 +568,12 @@ exports[` select values and save 1`] = ` class="list-container" >
select values and save 1`] = `
- Camel Netty4 test HTTP + + Camel Netty4 test HTTP +
select values and save 1`] = `
show results for valid search 1`] = ` aria-invalid="false" aria-label="Search extensions" class="pf-c-form-control search-extensions-input" + id="extension-search" placeholder="" - type="text" + type="search" value="CDI" />
@@ -790,11 +813,12 @@ exports[` show results for valid search 1`] = ` class="list-container" >
show results for valid search 1`] = `
- ArC + + ArC +
show results for valid search 1`] = `
show results for valid search 1`] = `
show results for valid search 1`] = `
- A CDI in name test + + A CDI in name test +
show results for valid search 1`] = `
renders the InfoPicker correctly 1`] = ` value="test" />
+
+ + +
renders the InfoPicker correctly 1`] = ` value="org.test" />
+
+ + +
(d: ExtensionEntry) => { return true; } return d.name.toLowerCase().includes(filterLowerCase) - || d.labels.filter(l => l.startsWith(filterLowerCase)).length > 0 + || d.keywords.filter(l => l.startsWith(filterLowerCase)).length > 0 || (d.category && d.category.toLowerCase().startsWith(filterLowerCase)) || shortName.startsWith(filterLowerCase); } @@ -25,8 +25,8 @@ export const sortFunction = (filter: string) => (a: ExtensionEntry, b: Extension if (startWithAShortName !== startWithBShortName) { return startWithAShortName ? -1 : 1; } - const startWithOneOfALabel = a.labels.filter(l => l.startsWith(filterLowerCase)).length > 0; - const startWithOneOfBLabel = b.labels.filter(l => l.startsWith(filterLowerCase)).length > 0; + const startWithOneOfALabel = a.keywords.filter(l => l.startsWith(filterLowerCase)).length > 0; + const startWithOneOfBLabel = b.keywords.filter(l => l.startsWith(filterLowerCase)).length > 0; if (startWithOneOfALabel !== startWithOneOfBLabel) { return startWithOneOfALabel ? -1 : 1; } diff --git a/src/main/frontend/src/code-quarkus/pickers/extensions-picker.scss b/src/main/frontend/src/code-quarkus/pickers/extensions-picker.scss index a96b3ed2e..7dee69461 100644 --- a/src/main/frontend/src/code-quarkus/pickers/extensions-picker.scss +++ b/src/main/frontend/src/code-quarkus/pickers/extensions-picker.scss @@ -1,74 +1,33 @@ +@import "../config.scss"; + .extensions-picker { display: flex; - color: white; - - .control-container { - flex: 0 0 350px; - - .pf-c-form__group { - svg { - margin: 0 10px; - } - } - - input.pf-c-form-control { - border: 1px solid #9aff2d; - padding-left: 40px; - margin-left: -40px; - font-size: 14px; - width: 100%; - background: transparent; - color: white; - font-family: "PT Mono", Regular; - padding-bottom: 4px; - } - - input.pf-c-form-control:focus { - outline: none; - border: 1px solid #4695eb; - } - - h4 { - border-bottom: 1px solid #ff004a; - font-weight: bold; - margin-top: 40px; - margin-bottom: 20px; - } - .extension-list { - display: grid; - grid-template-columns: auto 30px; - .extension-item { - cursor: pointer; - display: contents; - } - } - } + color: $main; .extension-item { + display: flex; + width: 100%; + line-height: 30px; cursor: pointer; - svg { pointer-events: none; } - .extension-selector { - color: #fff; - } - &.selected { .extension-selector { - color: #71aeef; + color: $alert; } } - &.active.selected { + &.hover.selected { .extension-selector { - color: #fff; + color: $main; } } .extension-remove { - color: #ff004a; + color: $secondary; + height: 30px; } & div:focus { @@ -83,13 +42,26 @@ } } + .extension-summary { + overflow: hidden; + display: flex; + } .extension-name { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - font-size: 0.9rem; + font-size: 0.8rem; font-weight: bold; height: 24px; + outline: none; + } + + .extension-status { + font-size: 0.6rem; + line-height: 20px; + margin: 5px 5px; + padding: 0 3px; + border: 1px solid $highlight; } .extension-details { @@ -101,6 +73,10 @@ white-space: nowrap; overflow: hidden; font-size: 0.8rem; + + @media screen and (max-width: 1200px) { + display: none; + } } .extension-gav { @@ -117,61 +93,115 @@ } } } -} -.result-container { - margin-left: 30px; - flex-grow: 1; + .list-container { + .extension-category { + border-bottom: 1px solid $highlight; + font-weight: bold; + font-size: 1.1rem; + margin: 10px 0 5px 0; + height: 30px; + } + + .extension-category:first-child { + margin: 0 0 5px 0; + } - @media screen and (max-width: 900px) { - margin: 5px; - } + .extension-item { + height: 30px; + color: $main; - .extension-search-clear { - border-bottom: 1px solid #4695eb; - font-weight: bold; - font-size: 1.1rem; - margin: 0 0 5px 0; - height: 30px; - .pf-c-button.pf-m-link { - color: #ff004a; - padding: 0 3px; + & > div { + margin: 0 10px; + } + + &.keyboard-actived { + color: $highlight; + } + + .extension-selector { + flex-basis: 30px; + } + .extension-description { + flex: 1; + } + + .extension-summary { + flex-basis: 350px; + + @media screen and (max-width: 1200px) { + flex-grow: 1; + } + } + + .extension-gav { + flex-basis: 30px; + color: $main; + } + + &.readonly { + cursor: initial; + } } } -} -.list-container { - display: grid; - grid-template-columns: 30px 300px auto 30px; + .control-container { + flex: 0 0 350px; + overflow: hidden; - @media screen and (max-width: 900px) { - grid-template-columns: 150px auto; - } + .pf-c-form__group { + svg { + margin: 0 10px; + } + } - .extension-category { - grid-column: 1 / span 4; - border-bottom: 1px solid #4695eb; - font-weight: bold; - font-size: 1.1rem; - margin: 10px 0 5px 0; - height: 30px; + input.pf-c-form-control { + border: 1px solid $main; + padding-left: 40px; + margin-left: -36px; + font-size: 14px; + width: 100%; + background: transparent; + color: $main; + font-family: "PT Mono", Regular; + padding-bottom: 4px; + } - @media screen and (max-width: 900px) { - grid-column: 1 / span 2; + input.pf-c-form-control:focus { + outline: none; + border: 1px solid $highlight; } - } - .extension-category:first-child { - margin: 0 0 5px 0; + h4 { + border-bottom: 1px solid $readonly; + font-weight: bold; + margin-top: 40px; + margin-bottom: 20px; + } + .extension-summary { + flex-grow: 1; + } } - .extension-item { - cursor: pointer; - color: white; - display: contents; + .result-container { + margin-left: 30px; + flex-grow: 1; + overflow: hidden; - & > div { - margin: 0 10px; + @media screen and (max-width: 900px) { + margin: 5px; + } + + .extension-search-clear { + border-bottom: 1px solid $highlight; + font-weight: bold; + font-size: 1.1rem; + margin: 0 0 5px 0; + height: 30px; + .pf-c-button.pf-m-link { + color: $secondary; + padding: 0 3px; + } } } } @@ -185,7 +215,7 @@ display: table; .extension-item { display: table-row; - .extension-name { + .extension-summary { display: table-cell; width: 90%; } @@ -204,20 +234,21 @@ display: table-cell; width: 30px; } - .extension-name { + .extension-summary { display: table-cell; width: 300px; } .extension-details { + width: 100%; .extension-description { display: inline; } .extension-gav { float: right; - } + } } .extension-item > div { margin: 0; } } -} \ No newline at end of file +} diff --git a/src/main/frontend/src/code-quarkus/pickers/extensions-picker.tsx b/src/main/frontend/src/code-quarkus/pickers/extensions-picker.tsx index 7841e9b18..f7ecd88b4 100644 --- a/src/main/frontend/src/code-quarkus/pickers/extensions-picker.tsx +++ b/src/main/frontend/src/code-quarkus/pickers/extensions-picker.tsx @@ -1,23 +1,25 @@ import { Button, FormGroup, TextInput, Tooltip } from "@patternfly/react-core"; -import { CheckSquareIcon, OutlinedSquareIcon, SearchIcon, TrashAltIcon, SquareIcon } from "@patternfly/react-icons"; -import React, { useState } from "react"; -import { useHotkeys } from 'react-hotkeys-hook'; +import { CheckSquareIcon, OutlinedSquareIcon, SearchIcon, TrashAltIcon } from "@patternfly/react-icons"; +import classNames from 'classnames'; import hotkeys from 'hotkeys-js'; +import React, { useState, KeyboardEvent } from "react"; +import { useHotkeys } from 'react-hotkeys-hook'; import { InputProps, useAnalytics } from '../../core'; import { CopyToClipboard } from '../copy-to-clipboard'; -import { processEntries } from './extensions-picker-helpers'; import { QuarkusBlurb } from '../quarkus-blurb'; +import { processEntries } from './extensions-picker-helpers'; import './extensions-picker.scss'; - export interface ExtensionEntry { id: string; name: string; - labels: string[]; + keywords: string[]; description?: string; shortName?: string; category: string; order: number, + default: boolean, + status: string } export interface ExtensionsPickerValue { @@ -27,74 +29,86 @@ export interface ExtensionsPickerValue { interface ExtensionsPickerProps extends InputProps { entries: ExtensionEntry[]; placeholder: string; + buildTool: string; filterFunction?(d: ExtensionEntry): boolean; } interface ExtensionProps extends ExtensionEntry { selected: boolean; - actived: boolean; + keyboardActived: boolean; detailed?: boolean; + default: boolean; + buildTool: string; onClick(id: string): void; } function Extension(props: ExtensionProps) { - const [active, setActive] = useState(false); - const activate = () => setActive(true); - const desactivate = () => setActive(false); + const [hover, setHover] = useState(false); const onClick = () => { + if (props.default) { + return; + } props.onClick(props.id); - setActive(false); + setHover(false); }; const activationEvents = { onClick, - onMouseEnter: activate, - onMouseLeave: desactivate, + onMouseEnter: () => setHover(true), + onMouseLeave: () => setHover(false), }; const description = props.description || '...'; - const descTooltip =
{props.name}

{description}

; - const tooltip = props.detailed ? + const descTooltip = props.default ?
{props.name}

{description}(This extension is included by default)

:
{props.name}

{description}

; + let tooltip = props.detailed && !props.default ?
{props.selected ? 'Remove' : 'Add'} the extension {props.name}
: descTooltip; + + const buildTool = props.buildTool || 'MAVEN'; const addMvnExt = `./mvnw quarkus:add-extension -Dextensions="${props.id}"`; + const selected = props.selected || props.default; + const addGradleExt = `./gradlew addExtension --extensions="${props.id}"`; + const addExtCmd = buildTool === 'GRADLE' ? addGradleExt : addMvnExt; return ( -
+
{props.detailed && ( -
- {!props.selected && !(active || props.actived) && } - {(active || props.selected) && } - {props.actived && !props.selected && !active && } -
+ +
+ {!selected && !(hover) && } + {(hover || selected) && } +
+
)} - -
{props.name}
-
+ +
+ + {props.name} + + {props.status === 'preview' && PREVIEW} +
+ {!props.detailed && (
- {active && props.selected && } + {hover && props.selected && }
)} + {props.detailed && (
{description}
- Copy mvn command to clipboard:
$ {addMvnExt}} exitDelay={0} zIndex={100}> -
-
+
)}
@@ -110,12 +124,24 @@ export const ExtensionsPicker = (props: ExtensionsPickerProps) => { const entrySet = new Set(extensions); const entriesById: Map = new Map(props.entries.map(item => [item.id, item])); - hotkeys.filter = () => true + hotkeys.filter = (e) => { + const el = (e.target || e.srcElement) as any | undefined; + if (!el) { + return true; + } + var tagName = el && el.tagName; + return el.id === 'extension-search' || !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA'); + }; + const result = processEntries(filter, props.entries); - const add = (id: string) => { + const add = (index: number) => { + const id = result[index].id entrySet.add(id); props.onChange({ extensions: Array.from(entrySet) }); analytics.event('Picker', 'Add-Extension', id); + if (keyboardActived >= 0) { + setKeyBoardActived(index); + } }; const remove = (id: string) => { @@ -123,7 +149,11 @@ export const ExtensionsPicker = (props: ExtensionsPickerProps) => { props.onChange({ extensions: Array.from(entrySet) }); analytics.event('Picker', 'Remove-Extension', id) }; - + const onSearchKeyDown = (e: KeyboardEvent) => { + if (e.which === 38 || e.which === 40) { + e.preventDefault(); + } + }; const search = (f: string) => { if (!hasSearched) { analytics.event('Picker', 'Search-Extension') @@ -133,22 +163,25 @@ export const ExtensionsPicker = (props: ExtensionsPickerProps) => { setFilter(f); } - const flip = (id: string) => { - if(entrySet.has(id)) { - remove(id); + const flip = (index: number) => { + if (!result[index] || result[index].default) { + return; + } + if (entrySet.has(result[index].id)) { + remove(result[index].id); } else { - add(id); + add(index); } } - const result = processEntries(filter, props.entries); + useHotkeys('esc', () => setKeyBoardActived(-1)); useHotkeys('up', () => setKeyBoardActived(Math.max(0, keyboardActived - 1)), [keyboardActived]); useHotkeys('down', () => setKeyBoardActived(Math.min(result.length - 1, keyboardActived + 1)), [result, keyboardActived]); useHotkeys('space', (event) => { - if(keyboardActived > 0) { + if (keyboardActived >= 0) { event.preventDefault(); - flip(result[keyboardActived].id); + flip(keyboardActived); } }, [result, keyboardActived]); @@ -166,6 +199,9 @@ export const ExtensionsPicker = (props: ExtensionsPickerProps) => { > { extensions.map((ex, i) => ( flip(ex)} + onClick={() => remove(ex)} /> )) } @@ -203,10 +240,11 @@ export const ExtensionsPicker = (props: ExtensionsPickerProps) => { const ext = ( flip(ex.id)} + onClick={() => flip(i)} + buildTool={props.buildTool} detailed /> ); diff --git a/src/main/frontend/src/code-quarkus/pickers/info-picker.scss b/src/main/frontend/src/code-quarkus/pickers/info-picker.scss index af908bd4f..edaa3223d 100644 --- a/src/main/frontend/src/code-quarkus/pickers/info-picker.scss +++ b/src/main/frontend/src/code-quarkus/pickers/info-picker.scss @@ -6,15 +6,17 @@ .base-settings { order: 1; flex-grow: 1; - padding: 0 10px; + padding: 0 7px; align-self: flex-start; + margin: 0; } .toggle-panel { align-self: flex-start; order: 4; flex-grow: 1; - padding: 0 10px; + padding: 0 7x; + } .toggle-panel.open { diff --git a/src/main/frontend/src/code-quarkus/pickers/info-picker.tsx b/src/main/frontend/src/code-quarkus/pickers/info-picker.tsx index eb8df2217..4808cc0bb 100644 --- a/src/main/frontend/src/code-quarkus/pickers/info-picker.tsx +++ b/src/main/frontend/src/code-quarkus/pickers/info-picker.tsx @@ -1,16 +1,19 @@ -import React, { useEffect } from 'react'; +import React, { ChangeEvent, useEffect } from 'react'; import { ExtendedTextInput, InputPropsWithValidation, optionalBool, TogglePanel } from '../../core'; import './info-picker.scss'; +import { FormGroup, Tooltip } from "@patternfly/react-core"; export interface InfoPickerValue { groupId?: string; artifactId?: string; version?: string; packageName?: string; + buildTool?: string; } interface InfoPickerProps extends InputPropsWithValidation { showMoreOptions?: boolean; + quarkusVersion: string; } const ARTIFACTID_PATTERN = /^[a-z][a-z0-9-._]*$/; @@ -41,6 +44,8 @@ export const InfoPicker = (props: InfoPickerProps) => { const onArtifactIdChange = (newValue: string) => onInputChange({ ...value, artifactId: newValue }); const onVersionChange = (newValue: string) => onInputChange({ ...value, version: newValue }); const onPackageNameChange = (newValue: string) => onInputChange({ ...value, packageName: newValue }); + const onBuildToolChange = (event: ChangeEvent) => onInputChange({ ...value, buildTool: event.target.value }); + const configFileName = value.buildTool === 'MAVEN' ? 'pom.xml' : 'gradle.properties'; return (
@@ -70,6 +75,15 @@ export const InfoPicker = (props: InfoPickerProps) => { pattern={ARTIFACTID_PATTERN.source} isValid={isValidId(value.artifactId)} /> + + +
{optionalBool(props.showMoreOptions, true) && ( @@ -99,6 +113,20 @@ export const InfoPicker = (props: InfoPickerProps) => { pattern={GROUPID_PATTERN.source} isValid={isValidGroupId(value.packageName || value.groupId)} /> + + + +
)} diff --git a/src/main/frontend/src/core/extended-text-input.tsx b/src/main/frontend/src/core/extended-text-input.tsx index 1dabeaaf2..1c1f45644 100644 --- a/src/main/frontend/src/core/extended-text-input.tsx +++ b/src/main/frontend/src/core/extended-text-input.tsx @@ -9,7 +9,7 @@ export interface ExtendedTextInputProps extends TextInputProps { export function ExtendedTextInput(props: ExtendedTextInputProps) { const [isDirty, setIsDirty] = useState(false); - const { onChange, isValid, helperTextInvalid, label, ...rest } = props; + const { onChange, isValid, helperTextInvalid, label, className, ...rest } = props; const analytics = useAnalytics(); const valid = (!isDirty && !props.value) || isValid; const onChangeWithDirty = (value: string, event: FormEvent) => { @@ -27,6 +27,7 @@ export function ExtendedTextInput(props: ExtendedTextInputProps) { label={label} isValid={!helperTextInvalid ? undefined : valid} helperTextInvalid={helperTextInvalid} + className={className} > =0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -7763,10 +7690,10 @@ node-pre-gyp@^0.12.0: semver "^5.3.0" tar "^4" -node-releases@^1.1.29, node-releases@^1.1.36: - version "1.1.36" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.36.tgz#44b7cb8254138e87bdbfa47761d0f825e20900b4" - integrity sha512-ggXhX6QGyJSjj3r+6ml2LqqC28XOWmKtpb+a15/Zpr9V3yoNazxJNlcQDS9bYaid5FReEWHEgToH1mwoUceWwg== +node-releases@^1.1.29: + version "1.1.35" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.35.tgz#32a74a3cd497aa77f23d509f483475fd160e4c48" + integrity sha512-JGcM/wndCN/2elJlU0IGdVEJQQnJwsLbgPCFd2pY7V0mxf17bZ0Gb/lgOtL29ZQhvEX5shnVhxQyZz3ex94N8w== dependencies: semver "^6.3.0" @@ -7832,7 +7759,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -8375,11 +8302,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" - integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== - pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -8471,13 +8393,13 @@ popper.js@^1.14.6: integrity sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw== portfinder@^1.0.9: - version "1.0.25" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" - integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== + version "1.0.24" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.24.tgz#11efbc6865f12f37624b6531ead1d809ed965cfa" + integrity sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg== dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.1" + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" posix-character-classes@^0.1.0: version "0.1.1" @@ -9636,13 +9558,6 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" - integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== - dependencies: - picomatch "^2.0.4" - realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -10072,9 +9987,9 @@ schema-utils@^1.0.0: ajv-keywords "^3.1.0" schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.5.0.tgz#8f254f618d402cc80257486213c8970edfd7c22f" - integrity sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ== + version "2.4.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.4.1.tgz#e89ade5d056dc8bcaca377574bb4a9c4e1b8be56" + integrity sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w== dependencies: ajv "^6.10.2" ajv-keywords "^3.4.1" @@ -10853,9 +10768,9 @@ terser-webpack-plugin@1.4.1, terser-webpack-plugin@^1.4.1: worker-farm "^1.7.0" terser@^4.1.2: - version "4.3.9" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.9.tgz#e4be37f80553d02645668727777687dad26bbca8" - integrity sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA== + version "4.3.8" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.8.tgz#707f05f3f4c1c70c840e626addfdb1c158a17136" + integrity sha512-otmIRlRVmLChAWsnSFNO0Bfk6YySuBp6G9qrHiJwlLDd4mxe2ta4sjI7TzIR+W1nBMjilzrMcPOz9pSusgx3hQ== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -10973,13 +10888,6 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -11139,11 +11047,11 @@ uglify-js@3.4.x: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.6.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.3.tgz#1351533bbe22cc698f012589ed6bd4cbd971bff8" - integrity sha512-KfQUgOqTkLp2aZxrMbCuKCDGW9slFYu2A23A36Gs7sGzTLcRBDORdOi5E21KWHFIfkY8kzgi/Pr1cXCh0yIp5g== + version "3.6.1" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.1.tgz#ae7688c50e1bdcf2f70a0e162410003cf9798311" + integrity sha512-+dSJLJpXBb6oMHP+Yvw8hUgElz4gLTh82XuX68QiJVTXaE5ibl6buzhNkQdYhBlIhozWOC9ge16wyRmjG4TwVQ== dependencies: - commander "~2.20.3" + commander "2.20.0" source-map "~0.6.1" unicode-canonical-property-names-ecmascript@^1.0.4: @@ -11800,9 +11708,9 @@ ws@^6.1.2: async-limiter "~1.0.0" ws@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.0.tgz#422eda8c02a4b5dba7744ba66eebbd84bcef0ec7" - integrity sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg== + version "7.1.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.2.tgz#c672d1629de8bb27a9699eb599be47aeeedd8f73" + integrity sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg== dependencies: async-limiter "^1.0.0" diff --git a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt index a9f889abe..e4f9a5339 100644 --- a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt +++ b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt @@ -1,22 +1,21 @@ package io.quarkus.code +import io.quarkus.code.model.CodeQuarkusExtension import io.quarkus.code.model.Config -import io.quarkus.code.model.QuarkusExtension import io.quarkus.code.model.QuarkusProject -import io.quarkus.runtime.StartupEvent -import org.eclipse.microprofile.config.inject.ConfigProperty -import org.eclipse.microprofile.metrics.annotation.Counted +import io.quarkus.code.services.CodeQuarkusConfigManager +import io.quarkus.code.services.QuarkusExtensionCatalog +import io.quarkus.code.services.QuarkusProjectCreator import org.eclipse.microprofile.openapi.annotations.Operation import org.eclipse.microprofile.openapi.annotations.media.Content import org.eclipse.microprofile.openapi.annotations.media.Schema -import org.eclipse.microprofile.openapi.annotations.parameters.Parameter import org.eclipse.microprofile.openapi.annotations.responses.APIResponse -import java.io.IOException -import javax.enterprise.event.Observes import javax.inject.Inject -import javax.validation.constraints.NotEmpty -import javax.validation.constraints.Pattern -import javax.ws.rs.* +import javax.validation.Valid +import javax.ws.rs.BeanParam +import javax.ws.rs.GET +import javax.ws.rs.Path +import javax.ws.rs.Produces import javax.ws.rs.core.MediaType.APPLICATION_JSON import javax.ws.rs.core.Response @@ -24,123 +23,50 @@ import javax.ws.rs.core.Response @Path("/") class CodeQuarkusResource { - companion object { - const val GROUPID_PATTERN = "^([a-zA-Z_\$][a-zA-Z\\d_\$]*\\.)*[a-zA-Z_\$][a-zA-Z\\d_\$]*\$" - const val ARTIFACTID_PATTERN = "^[a-z][a-z0-9-._]*\$" - const val CLASSNAME_PATTERN = GROUPID_PATTERN - const val PATH_PATTERN = "^\\/([a-z0-9\\-._~%!\$&'()*+,;=:@]+\\/?)*\$" - } - @Inject - lateinit var projectCreator: QuarkusProjectCreator - - @ConfigProperty(name = "io.quarkus.code.quarkus-version") - lateinit var quarkusVersion: String - - @ConfigProperty(name = "io.quarkus.code.git-commit-id") - lateinit var gitCommitId: String - - @ConfigProperty(name = "io.quarkus.code.environment", defaultValue = "dev") - lateinit var environment: String + lateinit var configManager: CodeQuarkusConfigManager - @ConfigProperty(name = "io.quarkus.code.ga-tracking-id", defaultValue = "") - lateinit var gaTrackingId: String - - @ConfigProperty(name = "io.quarkus.code.sentry-dsn", defaultValue = "") - lateinit var sentryDSN: String - - lateinit var extensions: ByteArray + @Inject + lateinit var extensionCatalog: QuarkusExtensionCatalog - fun onStart(@Observes ev: StartupEvent) { - val extensionsResource = CodeQuarkusResource::class.java.getResource("/quarkus/extensions.json") - ?: throw IOException("missing extensions.json file") - extensions = extensionsResource.readBytes() - } + @Inject + lateinit var projectCreator: QuarkusProjectCreator @GET @Path("/config") @Produces(APPLICATION_JSON) - @Operation(summary = "Get the Quarkus Launcher configuration", hidden = true) + @Operation(summary = "Get the Quarkus Launcher configuration (DEPRECATED to '/v1/...')", hidden = true) fun config(): Config { - return Config( - environment, - gaTrackingId, - sentryDSN, - quarkusVersion, - gitCommitId - ) + return configManager.getConfig() } + @GET @Path("/extensions") @Produces(APPLICATION_JSON) - @Operation(summary = "Get the Quarkus Launcher list of Quarkus extensions") + @Operation(summary = "Get the Quarkus Launcher list of Quarkus extensions (DEPRECATED to '/v1/...')") @APIResponse( responseCode = "200", description = "List of Quarkus extensions", content = [Content( mediaType = APPLICATION_JSON, - schema = Schema(implementation = QuarkusExtension::class) + schema = Schema(implementation = CodeQuarkusExtension::class) )] ) - @Counted(name = "countedExtensions", description = "How many time an application has been downloaded") - fun extensions(): Response { - return Response - .ok(extensions) - .type(APPLICATION_JSON) - .build() + fun extensions(): List { + return extensionCatalog.extensions } @GET @Path("/download") @Produces("application/zip") - @Operation(summary = "Download a custom Quarkus application with the provided settings") - fun download( - @DefaultValue(QuarkusProject.DEFAULT_GROUPID) - @NotEmpty - @Pattern(regexp=GROUPID_PATTERN) - @QueryParam("g") - @Parameter(name = "g", description = "GAV: groupId (default: ${QuarkusProject.DEFAULT_GROUPID})", required = false) - groupId: String, - - @DefaultValue(QuarkusProject.DEFAULT_ARTIFACTID) - @NotEmpty - @Pattern(regexp= ARTIFACTID_PATTERN) - @QueryParam("a") - @Parameter(name = "a", description = "GAV: artifactId (default: ${QuarkusProject.DEFAULT_ARTIFACTID})", required = false) - artifactId: String, - - @DefaultValue(QuarkusProject.DEFAULT_VERSION) - @NotEmpty - @QueryParam("v") - @Parameter(name = "v", description = "GAV: version (default: ${QuarkusProject.DEFAULT_VERSION})", required = false) - version: String, - - @DefaultValue(QuarkusProject.DEFAULT_CLASSNAME) - @NotEmpty - @QueryParam("c") - @Pattern(regexp=CLASSNAME_PATTERN) - @Parameter(name = "c", description = "The class name to use in the generated application (default: ${QuarkusProject.DEFAULT_CLASSNAME})", required = false) - className: String, - - @DefaultValue(QuarkusProject.DEFAULT_PATH) - @NotEmpty - @QueryParam("p") - @Pattern(regexp=PATH_PATTERN) - @Parameter(name = "p", description = "The path of the REST endpoint created in the generated application (default: ${QuarkusProject.DEFAULT_PATH})", required = false) - path: String, - - @QueryParam("e") - @Parameter(name = "e", description = "The set of extension ids that will be included in the generated application", required = false) - extensions: Set - ): Response { - val project = QuarkusProject(groupId, artifactId, version, className, path, extensions) + @Operation(summary = "Download a custom Quarkus application with the provided settings (DEPRECATED to '/v1/...')") + fun download(@Valid @BeanParam project: QuarkusProject): Response { return Response .ok(projectCreator.create(project)) .type("application/zip") - .header("Content-Disposition", "attachment; filename=\"$artifactId.zip\"") + .header("Content-Disposition", "attachment; filename=\"${project.artifactId}.zip\"") .build() } - } \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/QuarkusProjectCreator.kt b/src/main/kotlin/io/quarkus/code/QuarkusProjectCreator.kt deleted file mode 100644 index 1957accab..000000000 --- a/src/main/kotlin/io/quarkus/code/QuarkusProjectCreator.kt +++ /dev/null @@ -1,71 +0,0 @@ -package io.quarkus.code - -import io.quarkus.code.model.QuarkusProject -import io.quarkus.code.writer.CommonsZipProjectWriter -import io.quarkus.cli.commands.AddExtensions -import io.quarkus.cli.commands.CreateProject -import io.quarkus.generators.BuildTool -import java.io.ByteArrayOutputStream -import java.io.IOException -import javax.inject.Singleton - -@Singleton -open class QuarkusProjectCreator { - companion object { - const val RESOURCES_DIR = "/creator/mvnw" - const val MAVEN_WRAPPER_DIR = ".mvn/wrapper" - const val MAVEN_WRAPPER_JAR = "$MAVEN_WRAPPER_DIR/maven-wrapper.jar" - const val MAVEN_WRAPPER_PROPS = "$MAVEN_WRAPPER_DIR/maven-wrapper.properties" - const val MAVEN_WRAPPER_DOWNLOADER = "$MAVEN_WRAPPER_DIR/MavenWrapperDownloader.java" - const val MVNW_CMD = "mvnw.cmd" - const val MVNW = "mvnw" - } - - open fun create(project: QuarkusProject): ByteArray { - val baos = ByteArrayOutputStream() - baos.use { - val zipWriter = CommonsZipProjectWriter.createWriter(baos, project.artifactId) - zipWriter.use { - //FIXME use Quarkus CreateProject when updating version (remove duplication) - val sourceType = CreateProject.determineSourceType(project.extensions) - val context = mutableMapOf("path" to (project.path as Any)) - val success = CreateProject(zipWriter) - .groupId(project.groupId) - .artifactId(project.artifactId) - .version(project.version) - .sourceType(sourceType) - .buildTool(BuildTool.MAVEN) - .className(project.className) - .doCreateProject(context) - if (!success) { - throw IOException("Error during Quarkus project creation") - } - AddExtensions(zipWriter, BuildTool.MAVEN) - .addExtensions(project.extensions) - this.addMvnw(zipWriter) - } - } - return baos.toByteArray() - } - - private fun addMvnw(zipWrite: CommonsZipProjectWriter) { - zipWrite.mkdirs(MAVEN_WRAPPER_DIR) - writeResourceFile(zipWrite, MAVEN_WRAPPER_JAR) - writeResourceFile(zipWrite, MAVEN_WRAPPER_PROPS) - writeResourceFile(zipWrite, MAVEN_WRAPPER_DOWNLOADER) - writeResourceFile(zipWrite, MVNW_CMD, true) - writeResourceFile(zipWrite, MVNW, true) - } - - private fun writeResourceFile(zipWrite: CommonsZipProjectWriter, filePath: String, allowExec: Boolean = false) { - if (!zipWrite.exists(filePath)) { - val resourcePath = "$RESOURCES_DIR/$filePath" - val resource = QuarkusProjectCreator::class.java.getResource(resourcePath) - ?: throw IOException("missing resource $resourcePath") - val fileAsBytes = - resource.readBytes() - zipWrite.write(filePath, fileAsBytes, allowExec) - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/model/CodeQuarkusExtension.kt b/src/main/kotlin/io/quarkus/code/model/CodeQuarkusExtension.kt new file mode 100644 index 000000000..2e531ac42 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/model/CodeQuarkusExtension.kt @@ -0,0 +1,16 @@ +package io.quarkus.code.model + +data class CodeQuarkusExtension( + val id: String, + val name: String, + val description: String?, + val shortName: String?, + val category: String, + val status: String, + val default: Boolean, + val keywords: List, + val order: Int, + + @Deprecated(message = "has been replaced", replaceWith = ReplaceWith("keywords")) + val labels: List +) \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/model/Config.kt b/src/main/kotlin/io/quarkus/code/model/Config.kt index 02e817d7b..c029f3bca 100644 --- a/src/main/kotlin/io/quarkus/code/model/Config.kt +++ b/src/main/kotlin/io/quarkus/code/model/Config.kt @@ -5,5 +5,5 @@ data class Config( val gaTrackingId: String, val sentryDSN: String, val quarkusVersion: String, - val gitCommitId: String + val gitCommitId: String? ) \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/model/QuarkusExtension.kt b/src/main/kotlin/io/quarkus/code/model/QuarkusExtension.kt deleted file mode 100644 index f67d61cc7..000000000 --- a/src/main/kotlin/io/quarkus/code/model/QuarkusExtension.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.quarkus.code.model - -data class QuarkusExtension( - val id: String, - val name: String, - val description: String?, - val shortName: String?, - val category: String, - val order: Int, - val labels: Set -) \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/model/QuarkusProject.kt b/src/main/kotlin/io/quarkus/code/model/QuarkusProject.kt index 7634a8dd2..5c2e69462 100644 --- a/src/main/kotlin/io/quarkus/code/model/QuarkusProject.kt +++ b/src/main/kotlin/io/quarkus/code/model/QuarkusProject.kt @@ -1,18 +1,126 @@ package io.quarkus.code.model -data class QuarkusProject( - val groupId: String = DEFAULT_GROUPID, - val artifactId: String = DEFAULT_ARTIFACTID, - val version: String = DEFAULT_VERSION, - val className: String = DEFAULT_CLASSNAME, - val path: String = DEFAULT_PATH, - val extensions: Set = setOf() -) { +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter +import javax.validation.constraints.NotEmpty +import javax.validation.constraints.Pattern +import javax.ws.rs.DefaultValue +import javax.ws.rs.QueryParam + +class QuarkusProject { + companion object { const val DEFAULT_GROUPID = "org.acme" - const val DEFAULT_ARTIFACTID= "code-with-quarkus" + const val DEFAULT_ARTIFACTID = "code-with-quarkus" const val DEFAULT_VERSION = "1.0.0-SNAPSHOT" const val DEFAULT_CLASSNAME = "org.acme.ExampleResource" const val DEFAULT_PATH = "/hello" + const val DEFAULT_BUILDTOOL = "MAVEN" + + const val GROUPID_PATTERN = "^([a-zA-Z_\$][a-zA-Z\\d_\$]*\\.)*[a-zA-Z_\$][a-zA-Z\\d_\$]*\$" + const val ARTIFACTID_PATTERN = "^[a-z][a-z0-9-._]*\$" + const val CLASSNAME_PATTERN = GROUPID_PATTERN + const val PATH_PATTERN = "^\\/([a-z0-9\\-._~%!\$&'()*+,;=:@]+\\/?)*\$" + const val BUILDTOOL_PATTERN = "^(MAVEN)|(GRADLE)\$" + } + + constructor() + constructor(groupId: String = DEFAULT_GROUPID, + artifactId: String = DEFAULT_ARTIFACTID, + version: String = DEFAULT_VERSION, + className: String = DEFAULT_CLASSNAME, + path: String = DEFAULT_PATH, + buildTool: String = DEFAULT_BUILDTOOL, + extensions: Set = setOf()) { + this.groupId = groupId + this.artifactId = artifactId + this.version = version + this.className = className + this.extensions = extensions + this.path = path + this.buildTool = buildTool + } + + @DefaultValue(DEFAULT_GROUPID) + @NotEmpty + @Pattern(regexp = GROUPID_PATTERN) + @QueryParam("g") + @Parameter(name = "g", description = "GAV: groupId", required = false) + var groupId: String = DEFAULT_GROUPID + private set + + @DefaultValue(DEFAULT_ARTIFACTID) + @NotEmpty + @Pattern(regexp = ARTIFACTID_PATTERN) + @QueryParam("a") + @Parameter(name = "a", description = "GAV: artifactId", required = false) + var artifactId: String = DEFAULT_ARTIFACTID + private set + + @DefaultValue(DEFAULT_VERSION) + @NotEmpty + @QueryParam("v") + @Parameter(name = "v", description = "GAV: version", required = false) + var version: String = DEFAULT_VERSION + private set + + @DefaultValue(DEFAULT_CLASSNAME) + @NotEmpty + @QueryParam("c") + @Pattern(regexp = CLASSNAME_PATTERN) + @Parameter(name = "c", description = "The class name to use in the generated application", required = false) + var className: String = DEFAULT_CLASSNAME + private set + + @DefaultValue(DEFAULT_PATH) + @NotEmpty + @QueryParam("p") + @Pattern(regexp = PATH_PATTERN) + @Parameter(name = "p", description = "The path of the REST endpoint created in the generated application", required = false) + var path: String = DEFAULT_PATH + private set + + @DefaultValue(DEFAULT_BUILDTOOL) + @NotEmpty + @QueryParam("b") + @Pattern(regexp = BUILDTOOL_PATTERN) + @Parameter(name = "b", description = "The build tool to use (MAVEN or GRADLE)", required = false) + var buildTool: String = DEFAULT_BUILDTOOL + + @QueryParam("e") + @Parameter(name = "e", description = "The set of extension ids that will be included in the generated application", required = false) + var extensions: Set = setOf() + private set + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as QuarkusProject + + if (groupId != other.groupId) return false + if (artifactId != other.artifactId) return false + if (version != other.version) return false + if (className != other.className) return false + if (path != other.path) return false + if (buildTool != other.buildTool) return false + if (extensions != other.extensions) return false + + return true } + + override fun hashCode(): Int { + var result = groupId.hashCode() + result = 31 * result + artifactId.hashCode() + result = 31 * result + version.hashCode() + result = 31 * result + className.hashCode() + result = 31 * result + path.hashCode() + result = 31 * result + buildTool.hashCode() + result = 31 * result + extensions.hashCode() + return result + } + + override fun toString(): String { + return "QuarkusProject(groupId='$groupId', artifactId='$artifactId', version='$version', className='$className', path='$path', buildTool='$buildTool', extensions=$extensions)" + } + } \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt new file mode 100644 index 000000000..0920afd35 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt @@ -0,0 +1,46 @@ +package io.quarkus.code.services + +import io.quarkus.code.model.Config +import io.quarkus.code.services.QuarkusExtensionCatalog.Companion.bundledQuarkusVersion +import org.eclipse.microprofile.config.inject.ConfigProperty +import javax.inject.Singleton + +@Singleton +open class CodeQuarkusConfigManager { + + companion object { + const val GROUPID_PATTERN = "^([a-zA-Z_\$][a-zA-Z\\d_\$]*\\.)*[a-zA-Z_\$][a-zA-Z\\d_\$]*\$" + const val ARTIFACTID_PATTERN = "^[a-z][a-z0-9-._]*\$" + const val CLASSNAME_PATTERN = GROUPID_PATTERN + const val PATH_PATTERN = "^\\/([a-z0-9\\-._~%!\$&'()*+,;=:@]+\\/?)*\$" + } + + val quarkusVersion = bundledQuarkusVersion + + @ConfigProperty(name = "io.quarkus.code.git-commit-id", defaultValue = "test") + var gitCommitId: String? = null + private set + + @ConfigProperty(name = "io.quarkus.code.environment", defaultValue = "dev") + lateinit var environment: String + private set + + @ConfigProperty(name = "io.quarkus.code.ga-tracking-id", defaultValue = "") + lateinit var gaTrackingId: String + private set + + @ConfigProperty(name = "io.quarkus.code.sentry-dsn", defaultValue = "") + lateinit var sentryDSN: String + private set + + fun getConfig(): Config { + return Config( + environment, + gaTrackingId, + sentryDSN, + quarkusVersion, + gitCommitId + ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/services/QuarkusExtensionCatalog.kt b/src/main/kotlin/io/quarkus/code/services/QuarkusExtensionCatalog.kt new file mode 100644 index 000000000..36d99afc1 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/services/QuarkusExtensionCatalog.kt @@ -0,0 +1,29 @@ +package io.quarkus.code.services + +import com.google.common.base.Preconditions.checkState +import io.quarkus.code.model.CodeQuarkusExtension +import io.quarkus.platform.descriptor.resolver.json.QuarkusJsonPlatformDescriptorResolver +import org.eclipse.microprofile.config.spi.ConfigProviderResolver +import javax.inject.Singleton + +@Singleton +open class QuarkusExtensionCatalog { + + companion object { + @JvmStatic + val platformVersion = ConfigProviderResolver.instance().getConfig().getValue("io.quarkus.code.quarkus-platform-version", String::class.java) + + @JvmStatic + val bundledQuarkusVersion =ConfigProviderResolver.instance().getConfig().getValue("io.quarkus.code.quarkus-version", String::class.java) + + @JvmStatic + val descriptor = QuarkusJsonPlatformDescriptorResolver.newInstance().resolveFromBom("io.quarkus", "quarkus-universe-bom", platformVersion) + + init { + checkState(descriptor.quarkusVersion == bundledQuarkusVersion, "The platform version (%s) must be compatible with the bundled Quarkus version (%s != %s)", descriptor.bomVersion, descriptor.quarkusVersion, bundledQuarkusVersion) + } + } + + val extensions: List = QuarkusExtensionUtils.processExtensions(descriptor) + +} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/services/QuarkusExtensionUtils.kt b/src/main/kotlin/io/quarkus/code/services/QuarkusExtensionUtils.kt new file mode 100644 index 000000000..1d31d5808 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/services/QuarkusExtensionUtils.kt @@ -0,0 +1,109 @@ +package io.quarkus.code.services + +import com.google.common.collect.Lists +import io.quarkus.code.model.CodeQuarkusExtension +import io.quarkus.dependencies.Category +import io.quarkus.dependencies.Extension +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor +import java.util.concurrent.atomic.AtomicInteger + +object QuarkusExtensionUtils { + + @JvmStatic + fun processExtensions(descriptor: QuarkusPlatformDescriptor): List { + val list = Lists.newArrayList() + + val extById = descriptor.extensions.groupBy { toId(it) } + val extByCategory = getExtByCategory(descriptor) + val order = AtomicInteger() + descriptor.categories.forEach { cat -> + val pinnedList = getCategoryPinnedList(cat) + val pinnedSet = pinnedList.toSet() + pinnedList.forEach { id -> + val codeQExt = toCodeQuarkusExtension(extById[id]?.get(0), cat, order) + codeQExt?.let { list.add(it) } + + } + extByCategory[cat.id]?.sortedBy { e -> e.name }?.forEach { ext -> + if (!pinnedSet.contains(toId(ext))) { + val codeQExt = toCodeQuarkusExtension(ext, cat, order) + codeQExt?.let { list.add(it) } + } + } + } + return list + } + + private fun getCategoryPinnedList(cat: Category): List { + if (cat.metadata?.get(Category.MD_PINNED) == null || cat.metadata[Category.MD_PINNED] !is List<*>) { + return emptyList() + } + @Suppress("UNCHECKED_CAST") + return cat.metadata["pinned"] as List + } + + + @JvmStatic + fun toCodeQuarkusExtension(ext: Extension?, cat: Category, order: AtomicInteger): CodeQuarkusExtension? { + if (ext == null || ext.name == null) { + return null + } + if (isExtensionUnlisted(ext)) { + return null + } + val keywords = ext.keywords ?: emptyList() + return CodeQuarkusExtension( + "${ext.groupId}:${ext.artifactId}", + ext.name, + ext.description, + getExtensionShortName(ext), + cat.name, + getExtensionStatus(ext), + ext.artifactId == "quarkus-resteasy", + keywords, + order.getAndIncrement(), + keywords + ) + + } + + private fun getExtensionStatus(ext: Extension) = + ext.metadata?.get(Extension.MD_STATUS) as String? ?: "stable" + + private fun getExtensionShortName(ext: Extension) = + ext.metadata?.get(Extension.MD_SHORT_NAME) as String? + + private fun isExtensionUnlisted(ext: Extension): Boolean { + val unlisted = ext.metadata?.get(Extension.MD_UNLISTED) + if (unlisted !== null) { + if (unlisted is Boolean) { + return unlisted + } else if (unlisted is String) { + return unlisted.toBoolean() + } + } + return false + } + + @JvmStatic + fun toId(e: Extension) = "${e.groupId}:${e.artifactId}" + + @JvmStatic + fun getExtByCategory(descriptor: QuarkusPlatformDescriptor): Map> { + val extByCategory = HashMap>() + descriptor.extensions.forEach { + val categories = it.metadata?.get("categories") + if (categories is Collection<*>) { + categories.forEach { cat -> + if (cat is String) { + if (extByCategory[cat] == null) { + extByCategory[cat] = ArrayList() + } + extByCategory[cat]?.add(it) + } + } + } + } + return extByCategory + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/services/QuarkusProjectCreator.kt b/src/main/kotlin/io/quarkus/code/services/QuarkusProjectCreator.kt new file mode 100644 index 000000000..ebdaf0fdb --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/services/QuarkusProjectCreator.kt @@ -0,0 +1,96 @@ +package io.quarkus.code.services + +import io.quarkus.cli.commands.AddExtensions +import io.quarkus.cli.commands.CreateProject +import io.quarkus.code.model.QuarkusProject +import io.quarkus.code.writer.CommonsZipProjectWriter +import io.quarkus.generators.BuildTool +import io.quarkus.platform.tools.config.QuarkusPlatformConfig +import java.io.ByteArrayOutputStream +import java.io.IOException +import javax.inject.Singleton + +@Singleton +open class QuarkusProjectCreator { + companion object { + private const val MVNW_RESOURCES_DIR = "/creator/mvnw" + private const val MVNW_WRAPPER_DIR = ".mvn/wrapper" + private const val MVNW_WRAPPER_JAR = "$MVNW_WRAPPER_DIR/maven-wrapper.jar" + private const val MVNW_WRAPPER_PROPS = "$MVNW_WRAPPER_DIR/maven-wrapper.properties" + private const val MVNW_WRAPPER_DOWNLOADER = "$MVNW_WRAPPER_DIR/MavenWrapperDownloader.java" + private const val MVNW_CMD = "mvnw.cmd" + private const val MVNW = "mvnw" + + // Gradlew constants + private const val GRADLEW_RESOURCES_DIR = "/creator/gradlew" + private const val GRADLEW_WRAPPER_DIR = "gradle/wrapper" + private const val GRADLEW_WRAPPER_JAR = "$GRADLEW_WRAPPER_DIR/gradle-wrapper.jar" + private const val GRADLEW_WRAPPER_PROPS = "$GRADLEW_WRAPPER_DIR/gradle-wrapper.properties" + private const val GRADLEW_BAT = "gradlew.bat" + private const val GRADLEW = "gradlew" + } + + open fun create(project: QuarkusProject): ByteArray { + if (!QuarkusPlatformConfig.hasGlobalDefault()) { + QuarkusPlatformConfig.defaultConfigBuilder().setPlatformDescriptor(QuarkusExtensionCatalog.descriptor).build() + } + val baos = ByteArrayOutputStream() + baos.use { + val zipWriter = CommonsZipProjectWriter.createWriter(baos, project.artifactId) + zipWriter.use { + val sourceType = CreateProject.determineSourceType(project.extensions) + val context = mutableMapOf("path" to (project.path as Any)) + val buildTool = io.quarkus.generators.BuildTool.valueOf(project.buildTool) + val success = CreateProject(zipWriter) + .groupId(project.groupId) + .artifactId(project.artifactId) + .version(project.version) + .sourceType(sourceType) + .buildTool(buildTool) + .className(project.className) + .extensions(project.extensions) + .doCreateProject(context) + if (!success) { + throw IOException("Error during Quarkus project creation") + } + AddExtensions(zipWriter, buildTool) + .addExtensions(project.extensions) + if (buildTool == BuildTool.MAVEN) { + addMvnw(zipWriter) + } else if (buildTool == BuildTool.GRADLE) { + addGradlew(zipWriter) + } + } + } + return baos.toByteArray() + } + + private fun addMvnw(zipWrite: CommonsZipProjectWriter) { + zipWrite.mkdirs(MVNW_WRAPPER_DIR) + writeResourceFile(zipWrite, MVNW_RESOURCES_DIR, MVNW_WRAPPER_JAR) + writeResourceFile(zipWrite, MVNW_RESOURCES_DIR, MVNW_WRAPPER_PROPS) + writeResourceFile(zipWrite, MVNW_RESOURCES_DIR, MVNW_WRAPPER_DOWNLOADER) + writeResourceFile(zipWrite, MVNW_RESOURCES_DIR, MVNW_CMD, true) + writeResourceFile(zipWrite, MVNW_RESOURCES_DIR, MVNW, true) + } + + private fun addGradlew(zipWrite: CommonsZipProjectWriter) { + zipWrite.mkdirs(GRADLEW_WRAPPER_DIR) + writeResourceFile(zipWrite, GRADLEW_RESOURCES_DIR, GRADLEW_WRAPPER_JAR) + writeResourceFile(zipWrite, GRADLEW_RESOURCES_DIR, GRADLEW_WRAPPER_PROPS) + writeResourceFile(zipWrite, GRADLEW_RESOURCES_DIR, GRADLEW_BAT, true) + writeResourceFile(zipWrite, GRADLEW_RESOURCES_DIR, GRADLEW, true) + } + + private fun writeResourceFile(zipWrite: CommonsZipProjectWriter, resourcesDir: String, filePath: String, allowExec: Boolean = false) { + if (!zipWrite.exists(filePath)) { + val resourcePath = "$resourcesDir/$filePath" + val resource = QuarkusProjectCreator::class.java.getResource(resourcePath) + ?: throw IOException("missing resource $resourcePath") + val fileAsBytes = + resource.readBytes() + zipWrite.write(filePath, fileAsBytes, allowExec) + } + } + +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8ab05d6f8..83cbe1d67 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,4 +3,5 @@ quarkus.log.file.enable=false quarkus.http.cors=true quarkus.ssl.native=true io.quarkus.code.quarkus-version=${version.quarkus} +io.quarkus.code.quarkus-platform-version=${version.quarkus-platform} io.quarkus.code.git-commit-id=${git.commit.id.abbrev} \ No newline at end of file diff --git a/src/main/resources/creator/gradlew/gradle/wrapper/gradle-wrapper.jar b/src/main/resources/creator/gradlew/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..5c2d1cf01 Binary files /dev/null and b/src/main/resources/creator/gradlew/gradle/wrapper/gradle-wrapper.jar differ diff --git a/src/main/resources/creator/gradlew/gradle/wrapper/gradle-wrapper.properties b/src/main/resources/creator/gradlew/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..7c4388a92 --- /dev/null +++ b/src/main/resources/creator/gradlew/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/src/main/resources/creator/gradlew/gradlew b/src/main/resources/creator/gradlew/gradlew new file mode 100755 index 000000000..83f2acfdc --- /dev/null +++ b/src/main/resources/creator/gradlew/gradlew @@ -0,0 +1,188 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/src/main/resources/creator/gradlew/gradlew.bat b/src/main/resources/creator/gradlew/gradlew.bat new file mode 100644 index 000000000..24467a141 --- /dev/null +++ b/src/main/resources/creator/gradlew/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/resources/quarkus/extensions.json b/src/main/resources/quarkus/extensions.json deleted file mode 100644 index 78ec9dbeb..000000000 --- a/src/main/resources/quarkus/extensions.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":"io.quarkus:quarkus-arc","name":"ArC","labels":["arc","cdi","dependency-injection","di"],"description":"Build time CDI dependency injection","shortName":"CDI","category":"Core","order":2},{"id":"io.quarkus:quarkus-resteasy","name":"RESTEasy JAX-RS","labels":["resteasy","jaxrs","web","rest"],"description":"REST framework implementing JAX-RS and more","shortName":"jax-rs","category":"Web","order":4},{"id":"io.quarkus:quarkus-resteasy-jsonb","name":"RESTEasy JSON-B","labels":["resteasy-jsonb","jaxrs-json","resteasy-json","resteasy","jaxrs","json","jsonb"],"description":"JSON-B serialization support for RESTEasy","category":"Web","order":5},{"id":"io.quarkus:quarkus-resteasy-jackson","name":"RESTEasy Jackson","labels":["resteasy-jackson","jaxrs-json","resteasy-json","resteasy","jaxrs","json","jackson"],"description":"Jackson serialization support for RESTEasy","category":"Web","order":6},{"id":"io.quarkus:quarkus-smallrye-jwt","name":"SmallRye JWT","labels":["smallrye-jwt","jwt","json-web-token","rbac"],"description":"Secure your applications with JSON Web Token","category":"Web","order":7},{"id":"io.quarkus:quarkus-smallrye-openapi","name":"SmallRye OpenAPI","labels":["smallrye-openapi","openapi","open-api","swagger","swagger-ui"],"description":"Document your REST APIs with OpenAPI - comes with Swagger UI","category":"Web","order":8},{"id":"io.quarkus:quarkus-rest-client","name":"REST Client","labels":["rest-client","web-client","microprofile-rest-client"],"description":"Call REST services","category":"Web","order":9},{"id":"io.quarkus:quarkus-undertow","name":"Undertow Servlet","labels":["undertow","servlet"],"description":"Support for servlets","shortName":"servlet","category":"Web","order":10},{"id":"io.quarkus:quarkus-undertow-websockets","name":"Undertow WebSockets","labels":["undertow-websockets","undertow-websocket","websocket","websockets","web-socket","web-sockets"],"description":"WebSocket support","shortName":"websockets","category":"Web","order":11},{"id":"io.quarkus:quarkus-hibernate-validator","name":"Hibernate Validator","labels":["hibernate-validator","bean-validation","validation"],"description":"Validate data coming to your REST endpoints","shortName":"bean validation","category":"Web","order":12},{"id":"io.quarkus:quarkus-hibernate-orm","name":"Hibernate ORM","labels":["hibernate-orm","jpa","hibernate"],"description":"Define your persistent model with Hibernate ORM and JPA","shortName":"JPA","category":"Data","order":13},{"id":"io.quarkus:quarkus-hibernate-orm-panache","name":"Hibernate ORM with Panache","labels":["hibernate-orm-panache","panache","hibernate","jpa"],"description":"Define your persistent model in Hibernate ORM with Panache","category":"Data","order":14},{"id":"io.quarkus:quarkus-hibernate-validator","name":"Hibernate Validator","labels":["hibernate-validator","bean-validation","validation"],"description":"Validate your persistent model","shortName":"bean validation","category":"Data","order":15},{"id":"io.quarkus:quarkus-hibernate-search-elasticsearch","name":"Hibernate Search + Elasticsearch","labels":["hibernate-search","elasticsearch","full-text","search"],"description":"Automatically index your Hibernate entities in Elasticsearch","category":"Data","order":16},{"id":"io.quarkus:quarkus-flyway","name":"Flyway","labels":["flyway","schema"],"description":"Handle your database schema migrations","category":"Data","order":17},{"id":"io.quarkus:quarkus-mongodb-panache","name":"MongoDB with Panache","labels":["mongodb","mongo","nosql","panache"],"description":"Use an active record or repository pattern with MongoDB","category":"Data","order":18},{"id":"io.quarkus:quarkus-jdbc-h2","name":"JDBC Driver - H2","labels":["jdbc-h2","jdbc","h2"],"description":"H2 database connector","category":"Data","order":19},{"id":"io.quarkus:quarkus-jdbc-mariadb","name":"JDBC Driver - MariaDB","labels":["jdbc-mariadb","jdbc","mariadb"],"description":"MariaDB database connector","category":"Data","order":20},{"id":"io.quarkus:quarkus-jdbc-mysql","name":"JDBC Driver - MySQL","labels":["jdbc-mysql","jdbc","mysql"],"description":"MySQL database connector","category":"Data","order":21},{"id":"io.quarkus:quarkus-jdbc-postgresql","name":"JDBC Driver - PostgreSQL","labels":["jdbc-postgresql","jdbc","postgresql"],"description":"PostgreSQL database connector","category":"Data","order":22},{"id":"io.quarkus:quarkus-jdbc-mssql","name":"JDBC Driver - Microsoft SQL Server","labels":["jdbc-mssql","jdbc","mssql"],"description":"Microsoft SQL Server database connector","category":"Data","order":23},{"id":"io.quarkus:quarkus-jdbc-derby","name":"JDBC Driver - Derby","labels":["jdbc-derby","jdbc","derby"],"description":"Derby database connector","category":"Data","order":24},{"id":"io.quarkus:quarkus-infinispan-client","name":"Infinispan Client","labels":["infinispan-client","data-grid-client","infinispan"],"description":"Connect to the Infinispan data grid for distributed caching","category":"Data","order":25},{"id":"io.quarkus:quarkus-agroal","name":"Agroal - Database connection pool","labels":["agroal","database-connection-pool"],"description":"Pool your database connections (included in Hibernate ORM)","category":"Data","order":27},{"id":"io.quarkus:quarkus-reactive-mysql-client","name":"Reactive MySQL client","labels":["reactive","mysql"],"description":"A reactive client for the MySQL database","category":"Data","order":28},{"id":"io.quarkus:quarkus-reactive-pg-client","name":"Reactive PostgreSQL client","labels":["reactive","postgresql"],"description":"A reactive client for the PostgreSQL database","category":"Data","order":29},{"id":"io.quarkus:quarkus-mongodb-client","name":"MongoDB client","labels":["reactive","mongodb","mongo","nosql"],"description":"An imperative and reactive client for MongoDB","category":"Data","order":30},{"id":"io.quarkus:quarkus-neo4j","name":"Neo4j client","labels":["neo4j","graph","nosql"],"description":"A client for the Neo4j graph datastore","category":"Data","order":31},{"id":"io.quarkus:quarkus-amazon-dynamodb","name":"Amazon DynamoDB client","labels":["dynamodb","dynamo","aws","amazon"],"description":"A client for the Amazon DynamoDB datastore","category":"Data","order":32},{"id":"io.quarkus:quarkus-narayana-jta","name":"Narayana JTA - Transaction manager","labels":["narayana-jta","narayana","jta","transactions","transaction","tx","txs"],"description":"JTA transaction support (included in Hibernate ORM)","category":"Data","order":33},{"id":"io.quarkus:quarkus-narayana-stm","name":"Narayana STM - Software Transactional Memory","labels":["narayana-stm","narayana","stm"],"description":"Software Transactional Memory (stm) support","category":"Data","order":34},{"id":"io.quarkus:quarkus-smallrye-reactive-messaging","name":"SmallRye Reactive Messaging","labels":["smallrye-reactive-messaging","reactive-messaging","reactive"],"description":"Asynchronous messaging for Reactive Streams","category":"Messaging","order":35},{"id":"io.quarkus:quarkus-smallrye-reactive-messaging-kafka","name":"SmallRye Reactive Messaging - Kafka Connector","labels":["kafka","reactive-kafka"],"description":"Kafka reactive messaging connector","shortName":"kafka","category":"Messaging","order":36},{"id":"io.quarkus:quarkus-smallrye-reactive-messaging-amqp","name":"SmallRye Reactive Messaging - AMQP Connector","labels":["amqp","reactive-amqp"],"description":"AMQP reactive messaging connector","category":"Messaging","order":37},{"id":"io.quarkus:quarkus-smallrye-reactive-messaging-mqtt","name":"SmallRye Reactive Messaging - MQTT Connector","labels":["mqtt","reactive-mqtt"],"description":"MQTT reactive messaging connector","category":"Messaging","order":38},{"id":"io.quarkus:quarkus-kafka-client","name":"Apache Kafka Client","labels":["kafka"],"description":"A client for Apache Kafka","category":"Messaging","order":39},{"id":"io.quarkus:quarkus-kafka-streams","name":"Apache Kafka Streams","labels":["kafka","kafka-streams"],"description":"Implement stream processing applications based on Apache Kafka","category":"Messaging","order":40},{"id":"io.quarkus:quarkus-artemis-core","name":"Artemis Core","labels":["artemis-core","artemis"],"description":"Use ActiveMQ Artemis as message broker","category":"Messaging","order":41},{"id":"io.quarkus:quarkus-artemis-jms","name":"Artemis JMS","labels":["artemis-jms","artemis"],"description":"Use ActiveMQ Artemis as a JMS implementation","category":"Messaging","order":42},{"id":"io.quarkus:quarkus-vertx","name":"Eclipse Vert.x","labels":["eclipse-vert.x","vertx","vert.x","reactive"],"description":"Reactive application toolkit","category":"Reactive","order":43},{"id":"io.quarkus:quarkus-smallrye-reactive-streams-operators","name":"SmallRye Reactive Streams Operators","labels":["smallrye-reactive-streams-operators","smallrye-reactive-streams","reactive-streams-operators","reactive-streams","microprofile-reactive-streams","reactive"],"description":"Operators for Reactive Streams programming","shortName":"reactive streams","category":"Reactive","order":44},{"id":"io.quarkus:quarkus-smallrye-reactive-type-converters","name":"SmallRye Reactive Type Converters","labels":["smallrye-reactive-type-converters","reactive-type-converters","reactive-streams-operators","reactive-streams","microprofile-reactive-streams","reactive"],"description":"Converters for reactive types from various reactive programming libraries","category":"Reactive","order":45},{"id":"io.quarkus:quarkus-smallrye-context-propagation","name":"SmallRye Context Propagation","labels":["smallrye-context-propagation","microprofile-context-propagation","context-propagation","context","reactive"],"description":"SmallRye Context Propagation","shortName":"context propagation","category":"Reactive","order":46},{"id":"io.quarkus:quarkus-reactive-pg-client","name":"Reactive PostgreSQL client","labels":["reactive","postgresql"],"description":"A reactive client for the PostgreSQL database","category":"Reactive","order":47},{"id":"io.quarkus:quarkus-kogito","name":"Kogito","labels":["kogito","drools","jbpm"],"description":"Add business automation capabilities with Kogito","category":"Business Automation","order":48},{"id":"io.quarkus:quarkus-kubernetes","name":"Kubernetes","labels":["kubernetes","ap4k"],"description":"Generate Kubernetes resources from annotations","category":"Cloud","order":49},{"id":"io.quarkus:quarkus-kubernetes-client","name":"Kubernetes Client","labels":["kubernetes"],"description":"Interact with Kubernetes and develop Kubernetes Operators","category":"Cloud","order":50},{"id":"io.quarkus:quarkus-amazon-lambda","name":"AWS Lambda","labels":["lambda","amazon-lambda","aws-lambda","amazon","aws"],"description":"AWS Lambda support","category":"Cloud","order":51},{"id":"io.quarkus:quarkus-smallrye-fault-tolerance","name":"SmallRye Fault Tolerance","labels":["smallrye-fault-tolerance","fault-tolerance","microprofile-fault-tolerance","circuit-breaker","bulkhead"],"description":"Define fault-tolerant services","category":"Cloud","order":52},{"id":"io.quarkus:quarkus-smallrye-health","name":"SmallRye Health","labels":["smallrye-health","health-check","health","microprofile-health","microprofile-health-check"],"description":"Monitor service health","shortName":"health","category":"Cloud","order":53},{"id":"io.quarkus:quarkus-smallrye-metrics","name":"SmallRye Metrics","labels":["smallrye-metrics","metrics","metric","prometheus","monitoring"],"description":"Extract metrics out of your services","shortName":"metrics","category":"Observability","order":54},{"id":"io.quarkus:quarkus-smallrye-opentracing","name":"SmallRye OpenTracing","labels":["smallrye-opentracing","opentracing","tracing","distributed-tracing","jaeger"],"description":"Trace your services with Jaeger","category":"Observability","order":55},{"id":"io.quarkus:quarkus-oidc","name":"OpenID Connect","labels":["keycloak","oidc","openid","oauth2","openid-connect"],"description":"Secure your applications with OpenID Connect and Keycloak","category":"Security","order":56},{"id":"io.quarkus:quarkus-vault","name":"Vault","labels":["security","vault"],"description":"Store your credentials securely in HashiCorp Vault","category":"Security","order":57},{"id":"io.quarkus:quarkus-smallrye-jwt","name":"SmallRye JWT","labels":["smallrye-jwt","jwt","json-web-token","rbac"],"description":"Secure your applications with JSON Web Token","category":"Security","order":59},{"id":"io.quarkus:quarkus-elytron-security-oauth2","name":"Elytron Security OAuth 2.0","labels":["security","oauth2"],"description":"Secure your applications with OAuth2 opaque tokens","category":"Security","order":60},{"id":"io.quarkus:quarkus-elytron-security-jdbc","name":"Elytron Security JDBC Realm","labels":["security","jdbc-realm"],"description":"Secure your applications with username/password stored in a database","category":"Security","order":61},{"id":"io.quarkus:quarkus-jsonb","name":"JSON-B","labels":["jsonb","json-b","json","json-binding"],"description":"JSON Binding support","category":"Serialization","order":62},{"id":"io.quarkus:quarkus-jackson","name":"Jackson","labels":["jackson","json","jackson-databind"],"description":"Jackson Databind support","category":"Serialization","order":63},{"id":"io.quarkus:quarkus-jsonp","name":"JSON-P","labels":["jsonp","json-p","json","json-processing"],"description":"JSON Processing support","category":"Serialization","order":64},{"id":"io.quarkus:quarkus-resteasy-jsonb","name":"RESTEasy JSON-B","labels":["resteasy-jsonb","jaxrs-json","resteasy-json","resteasy","jaxrs","json","jsonb"],"description":"JSON-B serialization support for RESTEasy","category":"Serialization","order":65},{"id":"io.quarkus:quarkus-resteasy-jackson","name":"RESTEasy Jackson","labels":["resteasy-jackson","jaxrs-json","resteasy-json","resteasy","jaxrs","json","jackson"],"description":"Jackson serialization support for RESTEasy","category":"Serialization","order":66},{"id":"io.quarkus:quarkus-jaxb","name":"JAXB","labels":["resteasy-jaxb","resteasy","jaxb","xml"],"description":"XML serialization support","category":"Serialization","order":67},{"id":"io.quarkus:quarkus-mailer","name":"Mailer","labels":["email","smtp"],"description":"Send emails","category":"Miscellaneous","order":68},{"id":"io.quarkus:quarkus-scheduler","name":"Scheduler - tasks","labels":["scheduler","tasks","periodic-tasks"],"description":"Schedule jobs and tasks","category":"Miscellaneous","order":69},{"id":"io.quarkus:quarkus-tika","name":"Apache Tika","labels":["tika","parser"],"description":"Extract data from your documents with Apache Tika","category":"Miscellaneous","order":70},{"id":"io.quarkus:quarkus-jgit","name":"JGit","labels":["git","jgit"],"description":"Access your Git repositories","category":"Miscellaneous","order":71},{"id":"io.quarkus:quarkus-spring-di","name":"Quarkus Extension for Spring DI API","labels":["spring-di","spring"],"description":"Define your dependency injection with Spring DI","category":"Compatibility","order":72},{"id":"io.quarkus:quarkus-spring-web","name":"Quarkus Extension for Spring Web API","labels":["spring-web","spring"],"description":"Use Spring Web annotations to create your REST services","category":"Compatibility","order":73},{"id":"io.quarkus:quarkus-spring-data-jpa","name":"Quarkus Extension for Spring Data JPA API","labels":["spring-data","spring"],"description":"Use Spring Data JPA annotations to create your data access layer","category":"Compatibility","order":74},{"id":"io.quarkus:quarkus-kotlin","name":"Kotlin","labels":["kotlin"],"description":"Write your services in Kotlin","category":"Alternative languages","order":75},{"id":"io.quarkus:quarkus-scala","name":"Scala","labels":["scala"],"description":"Write your services in Scala","category":"Alternative languages","order":76}] \ No newline at end of file diff --git a/src/test/kotlin/io/quarkus/code/CodeQuarkusResourceTest.kt b/src/test/kotlin/io/quarkus/code/CodeQuarkusResourceTest.kt index 88618850e..bf64e4aca 100644 --- a/src/test/kotlin/io/quarkus/code/CodeQuarkusResourceTest.kt +++ b/src/test/kotlin/io/quarkus/code/CodeQuarkusResourceTest.kt @@ -1,6 +1,7 @@ package io.quarkus.code import io.quarkus.code.model.QuarkusProject +import io.quarkus.code.services.QuarkusProjectCreatorMock import io.quarkus.test.junit.QuarkusTest import io.restassured.RestAssured.given import org.hamcrest.CoreMatchers.* @@ -139,7 +140,6 @@ class CodeQuarkusResourceTest { .body("gaTrackingId", equalTo("")) .body("sentryDSN", equalTo("")) .body("quarkusVersion", notNullValue()) - .body("gitCommitId", notNullValue()) } @Test @@ -152,4 +152,30 @@ class CodeQuarkusResourceTest { .contentType(MediaType.APPLICATION_JSON) .body("$.size()", greaterThan(50)) } + + @Test + @DisplayName("Should generate a gradle project") + fun testGradle() { + given() + .`when`() + .get("/api/download?b=GRADLE&a=test-app-with-a-few-arg&v=1.0.0&e=io.quarkus:quarkus-smallrye-reactive-messaging&e=io.quarkus:quarkus-kafka-streams") + .then() + .statusCode(200) + .contentType("application/zip") + .header("Content-Disposition", "attachment; filename=\"test-app-with-a-few-arg.zip\"") + assertThat( + projectCreator.getCreatedProject(), equalTo( + QuarkusProject( + artifactId = "test-app-with-a-few-arg", + version = "1.0.0", + buildTool = "GRADLE", + extensions = setOf( + "io.quarkus:quarkus-kafka-streams", + "io.quarkus:quarkus-smallrye-reactive-messaging" + ) + + ) + ) + ) + } } \ No newline at end of file diff --git a/src/test/kotlin/io/quarkus/code/ProjectTestHelpers.kt b/src/test/kotlin/io/quarkus/code/services/ProjectTestHelpers.kt similarity index 98% rename from src/test/kotlin/io/quarkus/code/ProjectTestHelpers.kt rename to src/test/kotlin/io/quarkus/code/services/ProjectTestHelpers.kt index de3fe39cd..52e429bfa 100644 --- a/src/test/kotlin/io/quarkus/code/ProjectTestHelpers.kt +++ b/src/test/kotlin/io/quarkus/code/services/ProjectTestHelpers.kt @@ -1,4 +1,4 @@ -package io.quarkus.code +package io.quarkus.code.services import org.apache.commons.compress.archivers.ArchiveStreamFactory import org.apache.commons.compress.archivers.zip.ZipArchiveEntry diff --git a/src/test/kotlin/io/quarkus/code/services/QuarkusExtensionCatalogTest.kt b/src/test/kotlin/io/quarkus/code/services/QuarkusExtensionCatalogTest.kt new file mode 100644 index 000000000..4635f3ccb --- /dev/null +++ b/src/test/kotlin/io/quarkus/code/services/QuarkusExtensionCatalogTest.kt @@ -0,0 +1,113 @@ +package io.quarkus.code.services + +import io.quarkus.code.model.CodeQuarkusExtension +import io.quarkus.code.services.QuarkusExtensionUtils.processExtensions +import io.quarkus.platform.descriptor.loader.json.ArtifactResolver +import io.quarkus.platform.descriptor.loader.json.QuarkusJsonPlatformDescriptorLoaderContext +import io.quarkus.platform.descriptor.loader.json.impl.QuarkusJsonPlatformDescriptor +import io.quarkus.platform.descriptor.loader.json.impl.QuarkusJsonPlatformDescriptorLoaderImpl +import io.quarkus.platform.tools.DefaultMessageWriter +import io.quarkus.platform.tools.MessageWriter +import org.apache.maven.model.Dependency +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import java.io.File +import java.nio.file.Path +import java.util.ArrayList +import java.util.function.Function +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* +import java.io.IOException +import java.io.InputStream + +internal class QuarkusExtensionCatalogTest { + + @Test + internal fun textContent() { + val extensions = processExtensions(getTestDescriptor()) + assertThat(extensions[0], `is`(CodeQuarkusExtension( + "io.quarkus:quarkus-arc", + "ArC", + "Build time CDI dependency injection", + "CDI", + "Core", + "stable", + false, + listOf("arc", "cdi", "dependency-injection", "di"), + 0, + listOf("arc", "cdi", "dependency-injection", "di"))) + ) + assertThat(extensions[5], `is`(CodeQuarkusExtension( + "io.quarkus:quarkus-netty", + "Netty", + "Netty is a non-blocking I/O client-server framework. Used by Quarkus as foundation layer.", + null, + "Web", + "stable", + false, + listOf(), + 5, + listOf())) + ) + } + + @Test + internal fun testOrder() { + val extensions = processExtensions(getTestDescriptor()) + assertThat(extensions.map { it.name }.subList(0, 5), contains( + "ArC", + "RESTEasy JAX-RS", + "RESTEasy JSON-B", + "RESTEasy Jackson", + "Hibernate Validator")) + assertThat(extensions.map { it.name }.subList(extensions.size - 5, extensions.size), contains( + "Quarkus Extension for Spring DI API", + "Quarkus Extension for Spring Data JPA API", + "Quarkus Extension for Spring Web API", + "Kotlin", + "Scala")) + } + + + internal fun getTestDescriptor(): QuarkusJsonPlatformDescriptor { + val qpd = QuarkusJsonPlatformDescriptorLoaderImpl() + + val artifactResolver = object : ArtifactResolver { + + override fun process(groupId: String, artifactId: String, classifier: String, type: String, version: String, + processor: Function): T { + throw UnsupportedOperationException() + } + + override fun getManagedDependencies(groupId: String, artifactId: String, classifier: String?, + type: String, version: String): List { + return emptyList() + } + } + + val context = object : QuarkusJsonPlatformDescriptorLoaderContext(artifactResolver) { + override fun parseJson(parser: Function): T { + val resourceName = "fakeextensions.json" + + val `is` = javaClass.classLoader.getResourceAsStream(resourceName) + ?: throw IllegalStateException("Failed to locate $resourceName on the classpath") + + try { + return parser.apply(`is`) + } finally { + try { + `is`.close() + } catch (e: IOException) { + } + + } + } + + } + + val load = qpd.load(context) + + assertNotNull(load) + return load + } +} \ No newline at end of file diff --git a/src/test/kotlin/io/quarkus/code/QuarkusProjectCreatorMock.kt b/src/test/kotlin/io/quarkus/code/services/QuarkusProjectCreatorMock.kt similarity index 86% rename from src/test/kotlin/io/quarkus/code/QuarkusProjectCreatorMock.kt rename to src/test/kotlin/io/quarkus/code/services/QuarkusProjectCreatorMock.kt index 1c0e61aab..2bd0903f0 100644 --- a/src/test/kotlin/io/quarkus/code/QuarkusProjectCreatorMock.kt +++ b/src/test/kotlin/io/quarkus/code/services/QuarkusProjectCreatorMock.kt @@ -1,6 +1,7 @@ -package io.quarkus.code +package io.quarkus.code.services import io.quarkus.code.model.QuarkusProject +import io.quarkus.code.services.QuarkusProjectCreator import io.quarkus.test.Mock import java.util.concurrent.atomic.AtomicReference import javax.inject.Singleton diff --git a/src/test/kotlin/io/quarkus/code/QuarkusProjectCreatorTest.kt b/src/test/kotlin/io/quarkus/code/services/QuarkusProjectCreatorTest.kt similarity index 99% rename from src/test/kotlin/io/quarkus/code/QuarkusProjectCreatorTest.kt rename to src/test/kotlin/io/quarkus/code/services/QuarkusProjectCreatorTest.kt index 2dab00526..193d988c2 100644 --- a/src/test/kotlin/io/quarkus/code/QuarkusProjectCreatorTest.kt +++ b/src/test/kotlin/io/quarkus/code/services/QuarkusProjectCreatorTest.kt @@ -1,4 +1,4 @@ -package io.quarkus.code +package io.quarkus.code.services import io.quarkus.code.model.QuarkusProject import io.quarkus.maven.utilities.MojoUtils @@ -152,8 +152,6 @@ internal class QuarkusProjectCreatorTest { assertThat(resourceText, containsString("@Path(\"/test/it\")")) } - - @Test @DisplayName("Should create multiple project correctly") @Timeout(2) diff --git a/src/test/resources/fakeextensions.json b/src/test/resources/fakeextensions.json new file mode 100644 index 000000000..e777a0004 --- /dev/null +++ b/src/test/resources/fakeextensions.json @@ -0,0 +1,1573 @@ + +{ + "bom": { + "group-id": "io.quarkus", + "artifact-id": "quarkus-bom", + "version": "999-SNAPSHOT" + }, + "extensions": [ + { + "name": "Quarkus - Core", + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-core", + "version": "999-SNAPSHOT", + "description": "Build parent to bring in required dependencies" + }, + { + "name": "ArC", + "metadata": { + "short-name": "CDI", + "keywords": [ + "arc", + "cdi", + "dependency-injection", + "di" + ], + "guide": "https://quarkus.io/guides/cdi-reference", + "categories": [ + "core" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-arc", + "version": "999-SNAPSHOT", + "description": "Build time CDI dependency injection" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-caffeine", + "version": "999-SNAPSHOT", + "name": "Quarkus - Caffeine - Runtime", + "description": "A high performance caching library for Java 8+" + }, + { + "name": "JAXB", + "metadata": { + "keywords": [ + "resteasy-jaxb", + "resteasy", + "jaxb", + "xml" + ], + "categories": [ + "serialization" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jaxb", + "version": "999-SNAPSHOT", + "description": "XML serialization support" + }, + { + "name": "Jackson", + "metadata": { + "keywords": [ + "jackson", + "json" + ], + "categories": [ + "serialization" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jackson", + "version": "999-SNAPSHOT", + "description": "Jackson Databind support" + }, + { + "name": "JSON-B", + "metadata": { + "keywords": [ + "jsonb", + "json-b", + "json" + ], + "guide": "https://quarkus.io/guides/rest-json-guide", + "categories": [ + "serialization" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jsonb", + "version": "999-SNAPSHOT", + "description": "JSON Binding support" + }, + { + "name": "JSON-P", + "metadata": { + "keywords": [ + "jsonp", + "json-p", + "json" + ], + "categories": [ + "serialization" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jsonp", + "version": "999-SNAPSHOT", + "description": "JSON Processing support" + }, + { + "name": "Netty", + "metadata": { + "categories": [ + "web" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-netty", + "version": "999-SNAPSHOT", + "description": "Netty is a non-blocking I/O client-server framework. Used by Quarkus as foundation layer." + }, + { + "name": "Agroal - Database connection pool", + "metadata": { + "keywords": [ + "agroal", + "database-connection-pool" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-agroal", + "version": "999-SNAPSHOT", + "description": "Pool your database connections (included in Hibernate ORM)" + }, + { + "name": "Artemis Core", + "metadata": { + "keywords": [ + "artemis-core", + "artemis" + ], + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-artemis-core", + "version": "999-SNAPSHOT", + "description": "Use ActiveMQ Artemis as message broker" + }, + { + "name": "Artemis JMS", + "metadata": { + "keywords": [ + "artemis-jms", + "artemis" + ], + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-artemis-jms", + "version": "999-SNAPSHOT", + "description": "Use ActiveMQ Artemis as a JMS implementation" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-elasticsearch-rest-client", + "version": "999-SNAPSHOT", + "name": "Quarkus - Elasticsearch REST client - Runtime", + "description": "Elasticsearch REST client" + }, + { + "name": "Jaeger", + "metadata": { + "keywords": [ + "jaeger" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-security", + "version": "999-SNAPSHOT", + "description": "Security" + }, + { + "name": "Elytron Security", + "metadata": { + "keywords": [ + "security" + ], + "categories": [ + "security" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-elytron-security", + "version": "999-SNAPSHOT", + "description": "Secure your services" + }, + { + "name": "Properties File based Security", + "metadata": { + "keywords": [ + "security" + ], + "guide": "https://quarkus.io/guides/elytron-properties-guide" + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-elytron-security-properties-file", + "version": "999-SNAPSHOT", + "description": "Secure your applications using properties files" + }, + { + "name": "Elytron Security OAuth 2.0", + "metadata": { + "keywords": [ + "security", + "oauth2" + ], + "categories": [ + "security" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-elytron-security-oauth2", + "version": "999-SNAPSHOT", + "description": "Secure your applications with OAuth2 opaque tokens" + }, + { + "name": "OpenID Connect", + "metadata": { + "keywords": [ + "oauth2", + "openid-connect" + ], + "guide": "https://quarkus.io/guides/oidc-guide", + "categories": [ + "security" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-oidc", + "version": "999-SNAPSHOT", + "description": "Secure your applications with OpenID Connect and Keycloak" + }, + { + "name": "Keycloak Authorization", + "metadata": { + "keywords": [ + "oauth2", + "openid-connect", + "keycloak", + "authorization-services", + "policy-enforcer", + "fine-grained-permission", + "resource-based-authorization" + ], + "guide": "https://quarkus.io/guides/keycloak-authorization-guide" + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-keycloak-authorization", + "version": "999-SNAPSHOT", + "description": "Build parent to bring in required dependencies" + }, + { + "name": "Flyway", + "metadata": { + "keywords": [ + "flyway", + "database", + "data" + ], + "guide": "https://quarkus.io/guides/flyway-guide", + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-flyway", + "version": "999-SNAPSHOT", + "description": "Handle your database schema migrations" + }, + { + "name": "Hibernate ORM", + "metadata": { + "short-name": "JPA", + "keywords": [ + "hibernate-orm", + "jpa", + "hibernate" + ], + "guide": "https://quarkus.io/guides/hibernate-orm-guide", + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-hibernate-orm", + "version": "999-SNAPSHOT", + "description": "Define your persistent model with Hibernate ORM and JPA" + }, + { + "name": "Hibernate ORM with Panache", + "metadata": { + "keywords": [ + "hibernate-orm-panache", + "panache", + "hibernate", + "jpa" + ], + "guide": "https://quarkus.io/guides/hibernate-orm-panache-guide", + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-hibernate-orm-panache", + "version": "999-SNAPSHOT", + "description": "Define your persistent model in Hibernate ORM with Panache" + }, + { + "name": "MongoDB with Panache", + "metadata": { + "keywords": [ + "mongo", + "mongodb", + "nosql", + "datastore", + "panache" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-mongodb-panache", + "version": "999-SNAPSHOT", + "description": "Use an active record or repository pattern with MongoDB" + }, + { + "name": "Hibernate Search + Elasticsearch", + "metadata": { + "keywords": [ + "hibernate-search-elasticsearch", + "search", + "full-text", + "hibernate", + "elasticsearch" + ], + "guide": "https://quarkus.io/guides/hibernate-search-guide", + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-hibernate-search-elasticsearch", + "version": "999-SNAPSHOT", + "description": "Automatically index your Hibernate entities in Elasticsearch" + }, + { + "name": "Hibernate Validator", + "metadata": { + "short-name": "bean validation", + "keywords": [ + "hibernate-validator", + "bean-validation", + "validation" + ], + "guide": "https://quarkus.io/guides/validation-guide", + "categories": [ + "web", + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-hibernate-validator", + "version": "999-SNAPSHOT", + "description": "Validate data coming to your REST endpoints" + }, + { + "name": "Infinispan Client", + "metadata": { + "keywords": [ + "infinispan-client", + "data-grid-client", + "infinispan" + ], + "guide": "https://quarkus.io/guides/infinispan-client-guide", + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-infinispan-client", + "version": "999-SNAPSHOT", + "description": "Connect to the Infinispan data grid for distributed caching" + }, + { + "name": "Infinispan Embedded", + "metadata": { + "keywords": [ + "infinispan" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-infinispan-embedded", + "version": "999-SNAPSHOT", + "description": "Run an embedded Infinispan data grid server for distributed caching" + }, + { + "name": "Security", + "metadata": { + "keywords": [ + "security" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jaeger", + "version": "999-SNAPSHOT", + "description": "Trace your services with Jaeger" + }, + { + "name": "JDBC Driver - PostgreSQL", + "metadata": { + "keywords": [ + "jdbc-postgresql", + "jdbc", + "postgresql" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jdbc-postgresql", + "version": "999-SNAPSHOT", + "description": "PostgreSQL database connector" + }, + { + "name": "JDBC Driver - H2", + "metadata": { + "keywords": [ + "jdbc-h2", + "jdbc", + "h2" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jdbc-h2", + "version": "999-SNAPSHOT", + "description": "H2 database connector" + }, + { + "name": "JDBC Driver - MariaDB", + "metadata": { + "keywords": [ + "jdbc-mariadb", + "jdbc", + "mariadb" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jdbc-mariadb", + "version": "999-SNAPSHOT", + "description": "MariaDB database connector" + }, + { + "name": "JDBC Driver - Microsoft SQL Server", + "metadata": { + "keywords": [ + "jdbc-mssql", + "jdbc", + "mssql", + "sql-server" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jdbc-mssql", + "version": "999-SNAPSHOT", + "description": "Microsoft SQL Server database connector" + }, + { + "name": "JDBC Driver - MySQL", + "metadata": { + "keywords": [ + "jdbc-mysql", + "jdbc", + "mysql" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jdbc-mysql", + "version": "999-SNAPSHOT", + "description": "MySQL database connector" + }, + { + "name": "JDBC Driver - Derby", + "metadata": { + "keywords": [ + "jdbc-derby", + "jdbc", + "derby" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jdbc-derby", + "version": "999-SNAPSHOT", + "description": "Derby database connector" + }, + { + "name": "Apache Kafka Client", + "metadata": { + "keywords": [ + "kafka" + ], + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-kafka-client", + "version": "999-SNAPSHOT", + "description": "A client for Apache Kafka" + }, + { + "name": "Apache Kafka Streams", + "metadata": { + "keywords": [ + "kafka", + "kafka-streams" + ], + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-kafka-streams", + "version": "999-SNAPSHOT", + "description": "Implement stream processing applications based on Apache Kafka" + }, + { + "name": "SmallRye Health", + "metadata": { + "short-name": "health", + "keywords": [ + "smallrye-health", + "health-check", + "health", + "microprofile-health", + "microprofile-health-check" + ], + "guide": "https://quarkus.io/guides/health-guide", + "categories": [ + "cloud" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-health", + "version": "999-SNAPSHOT", + "description": "Monitor service health" + }, + { + "name": "SmallRye JWT", + "metadata": { + "keywords": [ + "smallrye-jwt", + "jwt", + "json-web-token", + "rbac" + ], + "guide": "https://quarkus.io/guides/jwt-guide", + "categories": [ + "web", + "security" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-jwt", + "version": "999-SNAPSHOT", + "description": "Secure your applications with JSON Web Token" + }, + { + "name": "SmallRye Context Propagation", + "metadata": { + "short-name": "context propagation", + "keywords": [ + "smallrye-context-propagation", + "microprofile-context-propagation", + "context-propagation", + "context", + "reactive" + ], + "categories": [ + "reactive" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-context-propagation", + "version": "999-SNAPSHOT", + "description": "SmallRye Context Propagation" + }, + { + "name": "SmallRye Reactive Streams Operators", + "metadata": { + "short-name": "reactive streams", + "keywords": [ + "smallrye-reactive-streams-operators", + "smallrye-reactive-streams", + "reactive-streams-operators", + "reactive-streams", + "microprofile-reactive-streams", + "reactive" + ], + "categories": [ + "reactive" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-reactive-streams-operators", + "version": "999-SNAPSHOT", + "description": "Operators for Reactive Streams programming" + }, + { + "name": "SmallRye Reactive Type Converters", + "metadata": { + "keywords": [ + "smallrye-reactive-type-converters", + "reactive-type-converters", + "reactive-streams-operators", + "reactive-streams", + "microprofile-reactive-streams", + "reactive" + ], + "categories": [ + "reactive" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-reactive-type-converters", + "version": "999-SNAPSHOT", + "description": "Converters for reactive types from various reactive programming libraries" + }, + { + "name": "SmallRye Reactive Messaging", + "metadata": { + "keywords": [ + "smallrye-reactive-messaging", + "reactive-messaging", + "reactive" + ], + "guide": "https://quarkus.io/guides/async-message-passing", + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-reactive-messaging", + "version": "999-SNAPSHOT", + "description": "Asynchronous messaging for Reactive Streams" + }, + { + "name": "SmallRye Reactive Messaging - Kafka Connector", + "metadata": { + "short-name": "kafka", + "keywords": [ + "kafka", + "reactive-kafka" + ], + "guide": "https://quarkus.io/guides/kafka-guide", + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-reactive-messaging-kafka", + "version": "999-SNAPSHOT", + "description": "Kafka reactive messaging connector" + }, + { + "name": "SmallRye Reactive Messaging - AMQP Connector", + "metadata": { + "keywords": [ + "amqp", + "reactive-amqp" + ], + "guide": "https://quarkus.io/guides/amqp-guide", + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-reactive-messaging-amqp", + "version": "999-SNAPSHOT", + "description": "AMQP reactive messaging connector" + }, + { + "name": "SmallRye Reactive Messaging - MQTT Connector", + "metadata": { + "keywords": [ + "mqtt", + "reactive-mqtt" + ], + "categories": [ + "messaging" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-reactive-messaging-mqtt", + "version": "999-SNAPSHOT", + "description": "MQTT reactive messaging connector" + }, + { + "name": "SmallRye Metrics", + "metadata": { + "short-name": "metrics", + "keywords": [ + "smallrye-metrics", + "metrics", + "metric", + "prometheus", + "monitoring" + ], + "guide": "https://quarkus.io/guides/metrics-guide", + "categories": [ + "observability" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-metrics", + "version": "999-SNAPSHOT", + "description": "Extract metrics out of your services" + }, + { + "name": "SmallRye OpenAPI", + "metadata": { + "keywords": [ + "smallrye-openapi", + "openapi", + "open-api" + ], + "guide": "https://quarkus.io/guides/openapi-swaggerui-guide", + "categories": [ + "web" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-openapi", + "version": "999-SNAPSHOT", + "description": "Document your REST APIs with OpenAPI - comes with Swagger UI" + }, + { + "name": "SmallRye OpenTracing", + "metadata": { + "keywords": [ + "smallrye-opentracing", + "opentracing", + "tracing", + "distributed-tracing", + "jaeger" + ], + "guide": "https://quarkus.io/guides/opentracing-guide", + "categories": [ + "observability" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-opentracing", + "version": "999-SNAPSHOT", + "description": "Trace your services with Jaeger" + }, + { + "name": "REST Client", + "metadata": { + "keywords": [ + "rest-client", + "web-client", + "microprofile-rest-client" + ], + "guide": "https://quarkus.io/guides/rest-client-guide", + "categories": [ + "web" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-rest-client", + "version": "999-SNAPSHOT", + "description": "Call REST services" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-resteasy-common", + "version": "999-SNAPSHOT", + "name": "Quarkus - RESTEasy - Common - Runtime", + "description": "REST framework implementing JAX-RS and more" + }, + { + "name": "RESTEasy JAX-RS", + "metadata": { + "short-name": "jax-rs", + "keywords": [ + "resteasy", + "jaxrs", + "web", + "rest" + ], + "guide": "https://quarkus.io/guides/rest-json-guide", + "categories": [ + "web" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-resteasy", + "version": "999-SNAPSHOT", + "description": "REST framework implementing JAX-RS and more" + }, + { + "name": "RESTEasy Jackson", + "metadata": { + "keywords": [ + "resteasy-jackson", + "jaxrs-json", + "resteasy-json", + "resteasy", + "jaxrs", + "json", + "jackson" + ], + "categories": [ + "web", + "serialization" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-resteasy-jackson", + "version": "999-SNAPSHOT", + "description": "Jackson serialization support for RESTEasy" + }, + { + "name": "RESTEasy JSON-B", + "metadata": { + "keywords": [ + "resteasy-jsonb", + "jaxrs-json", + "resteasy-json", + "resteasy", + "jaxrs", + "json", + "jsonb" + ], + "guide": "https://quarkus.io/guides/rest-json-guide", + "categories": [ + "web", + "serialization" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-resteasy-jsonb", + "version": "999-SNAPSHOT", + "description": "JSON-B serialization support for RESTEasy" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-resteasy-jaxb", + "version": "999-SNAPSHOT", + "name": "Quarkus - RESTEasy - JAXB - Runtime", + "description": "XML serialization support for RESTEasy" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-resteasy-server-common", + "version": "999-SNAPSHOT", + "name": "Quarkus - RESTEasy - Server common - Runtime", + "description": "RESTEasy Server common" + }, + { + "name": "Narayana JTA - Transaction manager", + "metadata": { + "keywords": [ + "narayana-jta", + "narayana", + "jta", + "transactions", + "transaction", + "tx", + "txs" + ], + "guide": "https://quarkus.io/guides/transaction-guide", + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-narayana-jta", + "version": "999-SNAPSHOT", + "description": "JTA transaction support (included in Hibernate ORM)" + }, + { + "name": "Undertow Servlet", + "metadata": { + "short-name": "servlet", + "keywords": [ + "undertow", + "servlet" + ], + "categories": [ + "web" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-undertow", + "version": "999-SNAPSHOT", + "description": "Support for servlets" + }, + { + "name": "SmallRye Fault Tolerance", + "metadata": { + "keywords": [ + "smallrye-fault-tolerance", + "fault-tolerance", + "microprofile-fault-tolerance", + "circuit-breaker", + "bulkhead" + ], + "categories": [ + "cloud" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-smallrye-fault-tolerance", + "version": "999-SNAPSHOT", + "description": "Define fault-tolerant services" + }, + { + "name": "Eclipse Vert.x - Core", + "metadata": { + "keywords": [ + "eclipse-vert.x", + "vertx", + "vert.x", + "reactive" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-vertx-core", + "version": "999-SNAPSHOT", + "description": "Vert.x Core" + }, + { + "name": "Eclipse Vert.x", + "metadata": { + "keywords": [ + "eclipse-vert.x", + "vertx", + "vert.x", + "reactive" + ], + "guide": "https://quarkus.io/guides/using-vertx", + "categories": [ + "reactive" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-vertx", + "version": "999-SNAPSHOT", + "description": "Reactive application toolkit" + }, + { + "name": "Eclipse Vert.x - HTTP", + "metadata": { + "keywords": [ + "eclipse-vert.x", + "vertx", + "vert.x", + "reactive", + "vertx-http" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-vertx-http", + "version": "999-SNAPSHOT", + "description": "Vert.x HTTP" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-vertx-web", + "version": "999-SNAPSHOT", + "name": "Quarkus - Vert.x Web - Runtime", + "description": "Vert.x Web" + }, + { + "name": "Reactive PostgreSQL client", + "metadata": { + "keywords": [ + "eclipse-vert.x", + "vertx", + "vert.x", + "reactive", + "database", + "data", + "postgresql" + ], + "guide": "https://quarkus.io/guides/reactive-sql-clients", + "categories": [ + "data", + "reactive" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-reactive-pg-client", + "version": "999-SNAPSHOT", + "description": "A reactive client for the PostgreSQL database" + }, + { + "name": "Reactive MySQL client", + "metadata": { + "keywords": [ + "eclipse-vert.x", + "vertx", + "vert.x", + "reactive", + "database", + "data", + "mysql" + ], + "guide": "https://quarkus.io/guides/reactive-sql-clients", + "categories": [ + "data", + "reactive" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-reactive-mysql-client", + "version": "999-SNAPSHOT", + "description": "A reactive client for the MySQL database" + }, + { + "name": "Mailer", + "metadata": { + "keywords": [ + "mail", + "mailer" + ], + "guide": "https://quarkus.io/guides/sending-emails", + "categories": [ + "miscellaneous" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-mailer", + "version": "999-SNAPSHOT", + "description": "Send emails" + }, + { + "name": "MongoDB client", + "metadata": { + "keywords": [ + "mongo", + "mongodb", + "nosql", + "datastore" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-mongodb-client", + "version": "999-SNAPSHOT", + "description": "An imperative and reactive client for MongoDB" + }, + { + "name": "Undertow WebSockets", + "metadata": { + "short-name": "websockets", + "keywords": [ + "undertow-websockets", + "undertow-websocket", + "websocket", + "websockets", + "web-socket", + "web-sockets" + ], + "guide": "https://quarkus.io/guides/websocket-guide", + "categories": [ + "web" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-undertow-websockets", + "version": "999-SNAPSHOT", + "description": "WebSocket support" + }, + { + "name": "Scheduler - tasks", + "metadata": { + "keywords": [ + "scheduler", + "tasks", + "periodic-tasks" + ], + "guide": "https://quarkus.io/guides/scheduled-guide", + "categories": [ + "miscellaneous" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-scheduler", + "version": "999-SNAPSHOT", + "description": "Schedule jobs and tasks" + }, + { + "name": "Quarkus Extension for Spring DI API", + "metadata": { + "keywords": [ + "spring-di" + ], + "guide": "https://quarkus.io/guides/spring-di-guide", + "categories": [ + "compatibility" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-spring-di", + "version": "999-SNAPSHOT", + "description": "Define your dependency injection with Spring DI" + }, + { + "name": "Quarkus Extension for Spring Web API", + "metadata": { + "keywords": [ + "spring-web" + ], + "guide": "https://quarkus.io/guides/spring-web-guide", + "categories": [ + "compatibility" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-spring-web", + "version": "999-SNAPSHOT", + "description": "Use Spring Web annotations to create your REST services" + }, + { + "name": "Quarkus Extension for Spring Data JPA API", + "metadata": { + "keywords": [ + "spring-data" + ], + "guide": "https://quarkus.io/guides/spring-data-jpa-guide", + "categories": [ + "compatibility" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-spring-data-jpa", + "version": "999-SNAPSHOT", + "description": "Use Spring Data JPA annotations to create your data access layer" + }, + { + "name": "Swagger UI", + "metadata": { + "keywords": [ + "swagger-ui" + ], + "guide": "https://quarkus.io/guides/openapi-swaggerui-guide" + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-swagger-ui", + "version": "999-SNAPSHOT", + "description": "Swagger UI" + }, + { + "name": "Kotlin", + "metadata": { + "keywords": [ + "kotlin" + ], + "guide": "https://quarkus.io/guides/kotlin", + "categories": [ + "alt-languages" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-kotlin", + "version": "999-SNAPSHOT", + "description": "Write your services in Kotlin" + }, + { + "name": "AWS Lambda", + "metadata": { + "keywords": [ + "lambda", + "aws", + "amazon" + ], + "categories": [ + "cloud" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-amazon-lambda", + "version": "999-SNAPSHOT", + "description": "AWS Lambda support" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-amazon-lambda-http", + "version": "999-SNAPSHOT", + "name": "Quarkus - Amazon Lambda HTTP - Runtime", + "description": "Allows Java applications written for a servlet container to run in AWS Lambda" + }, + { + "name": "Amazon DynamoDB client", + "metadata": { + "keywords": [ + "dynamodb", + "dynamo", + "aws", + "amazon" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-amazon-dynamodb", + "version": "999-SNAPSHOT", + "description": "A client for the Amazon DynamoDB datastore" + }, + { + "metadata": { + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-azure-functions-http", + "version": "999-SNAPSHOT", + "name": "Quarkus - HTTP Azure Functions - Runtime", + "description": "This package contains all Java interfaces and annotations to interact with Microsoft Azure functions runtime." + }, + { + "name": "Kubernetes", + "metadata": { + "keywords": [ + "kubernetes" + ], + "guide": "https://quarkus.io/guides/kubernetes-guide", + "categories": [ + "cloud" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-kubernetes", + "version": "999-SNAPSHOT", + "description": "Generate Kubernetes resources from annotations" + }, + { + "name": "Kubernetes Client", + "metadata": { + "keywords": [ + "kubernetes-client" + ], + "categories": [ + "cloud" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-kubernetes-client", + "version": "999-SNAPSHOT", + "description": "Interact with Kubernetes and develop Kubernetes Operators" + }, + { + "name": "Kogito", + "metadata": { + "keywords": [ + "kogito", + "drools", + "jbpm" + ], + "guide": "https://quarkus.io/guides/kogito-guide", + "categories": [ + "business-automation" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-kogito", + "version": "999-SNAPSHOT", + "description": "Add business automation capabilities with Kogito" + }, + { + "name": "Apache Tika", + "metadata": { + "keywords": [ + "tika", + "parser" + ], + "categories": [ + "miscellaneous" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-tika", + "version": "999-SNAPSHOT", + "description": "Extract data from your documents with Apache Tika" + }, + { + "name": "Neo4j client", + "metadata": { + "keywords": [ + "neo4j", + "graph", + "nosql", + "datastore" + ], + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-neo4j", + "version": "999-SNAPSHOT", + "description": "A client for the Neo4j graph datastore" + }, + { + "name": "Scala", + "metadata": { + "keywords": [ + "scala" + ], + "categories": [ + "alt-languages" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-scala", + "version": "999-SNAPSHOT", + "description": "Write your services in Scala" + }, + { + "name": "JGit", + "metadata": { + "keywords": [ + "git" + ], + "categories": [ + "miscellaneous" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-jgit", + "version": "999-SNAPSHOT", + "description": "Access your Git repositories" + }, + { + "name": "Narayana STM - Software Transactional Memory", + "metadata": { + "keywords": [ + "narayana-stm", + "narayana", + "stm", + "transactions", + "transaction", + "software-transactional-memory", + "tx", + "txs" + ], + "guide": "https://quarkus.io/guides/stm-guide", + "categories": [ + "data" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-narayana-stm", + "version": "999-SNAPSHOT", + "description": "Software Transactional Memory (stm) support" + }, + { + "name": "Elytron Security JDBC Realm", + "metadata": { + "keywords": [ + "security", + "jdbc" + ], + "guide": "https://quarkus.io/guides/security-jdbc-guide", + "categories": [ + "security" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-elytron-security-jdbc", + "version": "999-SNAPSHOT", + "description": "Secure your applications with username/password stored in a database" + }, + { + "name": "Vault", + "metadata": { + "keywords": [ + "vault", + "security" + ], + "guide": "https://quarkus.io/guides/vault-guide", + "categories": [ + "security" + ] + }, + "group-id": "io.quarkus", + "artifact-id": "quarkus-vault", + "version": "999-SNAPSHOT", + "description": "Store your credentials securely in HashiCorp Vault" + } + ], + "categories": [ + { + "name": "Core", + "id": "core", + "description": "Essential Quarkus components. Provided automatically" + }, + { + "name": "Web", + "id": "web", + "description": "Everything you need for REST endpoints, HTTP and web formats like JSON", + "metadata": { + "pinned": [ + "io.quarkus:quarkus-resteasy", + "io.quarkus:quarkus-resteasy-jsonb", + "io.quarkus:quarkus-resteasy-jackson" + ] + } + }, + { + "name": "Data", + "id": "data", + "description": "Accessing and managing your data (RDBMS, NoSQL, caching, transaction management, etc)", + "metadata": { + "pinned": [ + "io.quarkus:quarkus-hibernate-orm", + "io.quarkus:quarkus-hibernate-orm-panache", + "io.quarkus:quarkus-jdbc-postgresql", + "io.quarkus:quarkus-jdbc-mariadb", + "io.quarkus:quarkus-jdbc-mysql", + "io.quarkus:quarkus-jdbc-mssql", + "io.quarkus:quarkus-jdbc-h2", + "io.quarkus:quarkus-jdbc-derby" + ] + } + }, + { + "name": "Messaging", + "id": "messaging", + "description": "Send and receives message to various messaging ssytems (AMQP, KAfka etc)", + "metadata": { + "pinned": [ + "io.quarkus:quarkus-smallrye-reactive-messaging", + "io.quarkus:quarkus-smallrye-reactive-messaging-amqp", + "io.quarkus:quarkus-smallrye-reactive-messaging-kafka", + "io.quarkus:quarkus-smallrye-reactive-messaging-mqtt" + ] + } + }, + { + "name": "Reactive", + "id": "reactive", + "description": "Non blocking stack and connectors", + "metadata": { + "pinned": [ + "io.quarkus:quarkus-vertx" + ] + } + }, + { + "name": "Cloud", + "id": "cloud", + "description": "Useful for Cloud Native deployments platforms like Kubernetes and cloud providers", + "metadata": { + "pinned": [ + "io.quarkus:quarkus-kubernetes", + "io.quarkus:quarkus-smallrye-health", + "io.quarkus:quarkus-smallrye-fault-tolerance" + ] + } + }, + { + "name": "Observability", + "id": "observability", + "description": "Metrics, tracing, etc" + }, + { + "name": "Security", + "id": "security", + "description": "Everything you need to secure your application", + "metadata": { + "pinned": [ + "io.quarkus:quarkus-oidc", + "io.quarkus:quarkus-smallrye-jwt" + ] + } + }, + { + "name": "Integration", + "id": "integration", + "description": "Connectors to read to write from a skew of systems (file, S#, Twitter, etc)", + "metadata": { + "pinned": [ + "org.apache.camel.quarkus:camel-quarkus-core", + "org.apache.camel.quarkus:camel-quarkus-core-xml" + ] + } + }, + { + "name": "Business Automation", + "id": "business-automation", + "description": "Rules engine, BPM, etc" + }, + { + "name": "Serialization", + "id": "serialization", + "description": "Serializing and deserializing various formats" + }, + { + "name": "Miscellaneous", + "id": "miscellaneous", + "description": "Mixed bag of good stuff" + }, + { + "name": "Compatibility", + "id": "compatibility", + "description": "Support for alternative programming models on Quarkus" + }, + { + "name": "Alternative languages", + "id": "alt-languages", + "description": "Support for other JVM based languages" + } + ] +} \ No newline at end of file