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"
/>
+
+
+
+ Build Tool
+
+
+
+
+ Maven
+
+
+ Gradle (Preview)
+
+
+
renders the InfoPicker correctly 1`] = `
value="org.test"
/>
+
+
+
+ Quarkus Version
+
+
+
+
(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)}
/>
+
+
+ Maven
+ Gradle (Preview)
+
+
{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