Skip to content

Commit 6c34fd7

Browse files
authored
Merge pull request #50161 from phillip-kruger/dev-assistant-scheduler
Added Dev Assistant for scheduler extension
2 parents ecfa8cb + ca1a4f5 commit 6c34fd7

File tree

4 files changed

+274
-6
lines changed

4 files changed

+274
-6
lines changed

extensions/scheduler/deployment/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
<groupId>io.quarkus</groupId>
3535
<artifactId>quarkus-scheduler-dev</artifactId>
3636
</dependency>
37+
<dependency>
38+
<groupId>io.quarkus</groupId>
39+
<artifactId>quarkus-assistant-deployment-spi</artifactId>
40+
</dependency>
3741
<dependency>
3842
<groupId>io.quarkus</groupId>
3943
<artifactId>quarkus-vertx-http-deployment</artifactId>

extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/devui/SchedulerDevUIProcessor.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import java.util.List;
44

5+
import io.quarkus.assistant.runtime.dev.Assistant;
56
import io.quarkus.deployment.IsLocalDevelopment;
67
import io.quarkus.deployment.annotations.BuildProducer;
78
import io.quarkus.deployment.annotations.BuildStep;
89
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
10+
import io.quarkus.devui.spi.buildtime.BuildTimeActionBuildItem;
911
import io.quarkus.devui.spi.page.CardPageBuildItem;
1012
import io.quarkus.devui.spi.page.FooterPageBuildItem;
1113
import io.quarkus.devui.spi.page.Page;
@@ -26,6 +28,11 @@ void page(List<ScheduledBusinessMethodItem> scheduledMethods,
2628
.icon("font-awesome-solid:clock")
2729
.componentLink("qwc-scheduler-scheduled-methods.js")
2830
.staticLabel(String.valueOf(scheduledMethods.size())));
31+
32+
pageBuildItem.addPage(Page.assistantPageBuilder()
33+
.title("Cron Builder")
34+
.componentLink("qwc-scheduler-cron-builder.js"));
35+
2936
cardPages.produce(pageBuildItem);
3037

3138
WebComponentPageBuilder logPageBuilder = Page.webComponentPageBuilder()
@@ -35,9 +42,50 @@ void page(List<ScheduledBusinessMethodItem> scheduledMethods,
3542
footerPages.produce(new FooterPageBuildItem(logPageBuilder));
3643
}
3744

45+
@BuildStep(onlyIf = IsLocalDevelopment.class)
46+
void createBuildTimeActions(BuildProducer<BuildTimeActionBuildItem> buildTimeActionProducer) {
47+
BuildTimeActionBuildItem bta = new BuildTimeActionBuildItem();
48+
49+
bta.actionBuilder()
50+
.methodName("interpretCron")
51+
.assistantFunction((a, p) -> {
52+
Assistant assistant = (Assistant) a;
53+
54+
return assistant.assistBuilder()
55+
.userMessage(INTERPRET_CRON)
56+
.variables(p)
57+
.assist();
58+
}).build();
59+
60+
bta.actionBuilder()
61+
.methodName("createCron")
62+
.assistantFunction((a, p) -> {
63+
Assistant assistant = (Assistant) a;
64+
65+
return assistant.assistBuilder()
66+
.userMessage(CREATE_CRON)
67+
.variables(p)
68+
.assist();
69+
}).build();
70+
71+
buildTimeActionProducer.produce(bta);
72+
}
73+
3874
@BuildStep(onlyIf = IsLocalDevelopment.class)
3975
JsonRPCProvidersBuildItem rpcProvider() {
4076
return new JsonRPCProvidersBuildItem(SchedulerJsonRPCService.class);
4177
}
4278

79+
private static final String INTERPRET_CRON = """
80+
Can you please interpret this cron and describe it in plain English. Reply in markdown format in a field called markdown.
81+
82+
Here is the cron: {{cron}}
83+
""";
84+
85+
private static final String CREATE_CRON = """
86+
Can you create a valid cron expression for the following description: {{description}}
87+
88+
Reply with the valid cron in a field called cron. Add an example on how to use this with the quarkus-scheduler extenion in a field called example. For the example please use markdown.
89+
""";
90+
4391
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { LitElement, html, css} from 'lit';
2+
import { JsonRpc } from 'jsonrpc';
3+
import '@vaadin/text-area';
4+
import '@vaadin/dialog';
5+
import '@vaadin/progress-bar';
6+
import MarkdownIt from 'markdown-it';
7+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
8+
import { dialogRenderer } from '@vaadin/dialog/lit.js';
9+
10+
/**
11+
* This component shows the scheduled methods.
12+
*/
13+
export class QwcSchedulerCronBuilder extends LitElement {
14+
jsonRpc = new JsonRpc(this);
15+
16+
static styles = css`
17+
:host {
18+
display: flex;
19+
flex-direction: column;
20+
gap: 10px;
21+
height: 100%;
22+
padding-left: 5px;
23+
padding-right: 5px;
24+
}
25+
26+
.input {
27+
display: flex;
28+
gap: 5px;
29+
align-items: baseline;
30+
justify-content: space-between;
31+
}
32+
vaadin-text-area {
33+
width: 100%;
34+
}
35+
36+
`;
37+
38+
static properties = {
39+
_description: {state: true},
40+
_createCronLoading: {state: true},
41+
_cron: {state: true},
42+
_example: {state: true}
43+
};
44+
45+
constructor() {
46+
super();
47+
this._createCronLoading = false;
48+
this._cron = null;
49+
this._example = null;
50+
this._description = '';
51+
this.md = new MarkdownIt();
52+
}
53+
54+
connectedCallback() {
55+
super.connectedCallback();
56+
}
57+
58+
disconnectedCallback() {
59+
super.disconnectedCallback();
60+
}
61+
62+
render() {
63+
return html`${this._renderLoadingDialog()}
64+
${this._renderInput()}
65+
${this._renderOutput()}
66+
`;
67+
}
68+
69+
_renderInput(){
70+
return html`<div class="input">
71+
<vaadin-text-area
72+
min-rows="4"
73+
max-rows="8"
74+
label="Describe the cron you want to create"
75+
.value=${this._description ?? ''}
76+
@value-changed=${(e) => { this._description = e.detail.value; }}>
77+
</vaadin-text-area>
78+
${this._renderButton()}
79+
</div>`;
80+
}
81+
82+
_renderOutput(){
83+
if(this._cron){
84+
const htmlContent = this.md.render(this._example);
85+
return html`<div class="output">
86+
<h3>Cron:</h3>
87+
<code>${this._cron}</code>
88+
<h3>Example</h3>
89+
<div class="example">
90+
${unsafeHTML(htmlContent)}
91+
</div>
92+
</div>`;
93+
}
94+
}
95+
96+
_renderButton(){
97+
if(!this._createCronLoading){
98+
return html`<vaadin-button @click="${this._createCron}">Create cron</vaadin-button>`;
99+
}
100+
}
101+
102+
_renderLoadingDialog(){
103+
return html`
104+
<vaadin-dialog
105+
.opened="${this._createCronLoading}"
106+
@closed="${() => {
107+
this._createCronLoading = false;
108+
}}"
109+
${dialogRenderer(() => html`
110+
<label class="text-secondary" id="pblabel">Talking to the Dev Assistant ... please wait</label>
111+
<vaadin-progress-bar indeterminate aria-labelledby="pblabel"></vaadin-progress-bar>`, [])}
112+
></vaadin-dialog>`;
113+
}
114+
115+
_createCron(){
116+
this._createCronLoading = true;
117+
document.body.style.cursor = 'wait';
118+
this.jsonRpc.createCron({"description": this._description}).then(jsonResponse => {
119+
this._createCronLoading = false;
120+
document.body.style.cursor = 'default';
121+
this._cron = jsonResponse.result.cron;
122+
this._example = jsonResponse.result.example;
123+
});
124+
}
125+
126+
127+
128+
}
129+
customElements.define('qwc-scheduler-cron-builder', QwcSchedulerCronBuilder);

extensions/scheduler/deployment/src/main/resources/dev-ui/qwc-scheduler-scheduled-methods.js

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import { LitElement, html, css} from 'lit';
2+
import { observeState } from 'lit-element-state';
3+
import { assistantState } from 'assistant-state';
24
import { columnBodyRenderer } from '@vaadin/grid/lit.js';
5+
import { dialogHeaderRenderer, dialogRenderer } from '@vaadin/dialog/lit.js';
6+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
37
import { notifier } from 'notifier';
48
import { JsonRpc } from 'jsonrpc';
9+
import MarkdownIt from 'markdown-it';
510
import '@vaadin/grid';
611
import '@vaadin/text-field';
12+
import '@vaadin/dialog';
13+
import '@vaadin/progress-bar';
714

815
/**
916
* This component shows the scheduled methods.
1017
*/
11-
export class QwcSchedulerScheduledMethods extends LitElement {
18+
export class QwcSchedulerScheduledMethods extends observeState(LitElement) {
1219

1320
jsonRpc = new JsonRpc(this);
1421

@@ -46,15 +53,27 @@ export class QwcSchedulerScheduledMethods extends LitElement {
4653
.scheduler {
4754
padding-right: 20px;
4855
}
49-
5056
`;
5157

5258
static properties = {
5359
_scheduledMethods: {state: true},
5460
_filteredScheduledMethods: {state: true},
55-
_schedulerRunning: {state: true}
61+
_schedulerRunning: {state: true},
62+
_selectedCron: {state: true},
63+
_interpretDialogOpened: {state: true},
64+
_interpretCronLoading: {state: true},
65+
_interpretResult: {state: true}
5666
};
5767

68+
constructor() {
69+
super();
70+
this._interpretDialogOpened = false;
71+
this._interpretCronLoading = false
72+
this._selectedCron = null;
73+
this._interpretResult = null;
74+
this.md = new MarkdownIt();
75+
}
76+
5877
connectedCallback() {
5978
super.connectedCallback();
6079
this.jsonRpc.getData()
@@ -118,9 +137,9 @@ export class QwcSchedulerScheduledMethods extends LitElement {
118137
>
119138
<vaadin-icon slot="prefix" icon="font-awesome-solid:magnifying-glass"></vaadin-icon>
120139
</vaadin-text-field>
121-
`
140+
`;
122141

123-
return html`
142+
return html`${this._renderLoadingDialog()}${this._renderInterpretDialog()}
124143
<div class="topBar">
125144
${searchBox}
126145
${schedulerButton}
@@ -166,7 +185,7 @@ export class QwcSchedulerScheduledMethods extends LitElement {
166185
}
167186
}
168187
if (schedule.cron) {
169-
trigger = schedule.cronConfig ? html`${trigger} <code>${schedule.cron}</code> configured as <code>${schedule.cronConfig}</code>` : html`${trigger} <code>${schedule.cron}</code>`;
188+
trigger = schedule.cronConfig ? html`${trigger} <code>${schedule.cron}</code> configured as <code>${schedule.cronConfig}</code> ${this._renderInterpretCron(schedule.cronConfig)}` : html`${trigger} <code>${schedule.cron}</code> ${this._renderInterpretCron(schedule.cron)}`;
170189
} else {
171190
trigger = schedule.everyConfig ? html`${trigger} Every <code>${schedule.every}</code> configured as <code>${schedule.everyConfig}</code>` : html`${trigger} Every <code>${schedule.every}</code>`;
172191
}
@@ -181,6 +200,74 @@ export class QwcSchedulerScheduledMethods extends LitElement {
181200
return trigger;
182201
}
183202

203+
_renderInterpretCron(cron){
204+
if(assistantState.current.isConfigured){
205+
if(!this._interpretCronLoading){
206+
return html`<qui-assistant-button title="Interpret ${cron}" @click="${(e) => this._interpretCron(e, cron)}"></qui-assistant-button>`;
207+
}
208+
}
209+
}
210+
211+
_interpretCron(e, cron){
212+
document.body.style.cursor = 'wait';
213+
this._selectedCron = cron;
214+
215+
216+
this._interpretCronLoading = true;
217+
218+
this.jsonRpc.interpretCron({"cron": this._selectedCron}).then(jsonResponse => {
219+
this._interpretCronLoading = false;
220+
document.body.style.cursor = 'default';
221+
this._interpretResult = jsonResponse.result.markdown;
222+
this._interpretDialogOpened = true;
223+
224+
});
225+
}
226+
227+
_renderLoadingDialog(){
228+
return html`
229+
<vaadin-dialog
230+
.opened="${this._interpretCronLoading}"
231+
@closed="${() => {
232+
this._interpretCronLoading = false;
233+
}}"
234+
${dialogRenderer(() => html`
235+
<label class="text-secondary" id="pblabel">Talking to the Dev Assistant ... please wait</label>
236+
<vaadin-progress-bar indeterminate aria-labelledby="pblabel"></vaadin-progress-bar>`, [])}
237+
></vaadin-dialog>`;
238+
}
239+
240+
_renderInterpretDialog(){
241+
return html`
242+
<vaadin-dialog
243+
header-title="${`Interpret "${this._selectedCron}"`}"
244+
.opened="${this._interpretDialogOpened}"
245+
@closed="${() => {
246+
this._closeInterpretDialog();
247+
}}"
248+
${dialogRenderer(() => html`${this._renderInterpretDialogContents()}`, [])}
249+
${dialogHeaderRenderer(
250+
() => html`
251+
<vaadin-button theme="tertiary" @click="${this._closeInterpretDialog}">
252+
<vaadin-icon icon="font-awesome-solid:xmark"></vaadin-icon>
253+
</vaadin-button>
254+
`,
255+
[]
256+
)}
257+
></vaadin-dialog>`;
258+
}
259+
260+
_closeInterpretDialog(){
261+
this._interpretDialogOpened = false;
262+
this._selectedCron = null;
263+
this._interpretResult = null;
264+
}
265+
266+
_renderInterpretDialogContents(){
267+
const htmlContent = this.md.render(this._interpretResult);
268+
return html`${unsafeHTML(htmlContent)}`;
269+
}
270+
184271
_methodRenderer(scheduledMethod) {
185272
return html`
186273
<vaadin-button theme="small" @click=${() => this._executeMethod(scheduledMethod.methodDescription)}>

0 commit comments

Comments
 (0)