Skip to content

Commit

Permalink
Custom targets (#213)
Browse files Browse the repository at this point in the history
* Add target deletion button

* Add target creation button

* Extract modal subcomponent

* Handle null/undefined aliases or aliases equal to connectUrl

* Remove unused imports

* Support Enter to submit

* Display notification on target creation completion

* More informative notification if target deletion fails
  • Loading branch information
andrewazores authored Jun 24, 2021
1 parent 95ba0a5 commit 007b073
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 57 deletions.
100 changes: 65 additions & 35 deletions src/app/Shared/Services/Api.service.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
/*
* Copyright The Cryostat Authors
*
*
* The Universal Permissive License (UPL), Version 1.0
*
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or data
* (collectively the "Software"), free of charge and under any and all copyright
* rights in the Software, and any and all patent rights owned or freely
* licensable by each licensor hereunder covering either (i) the unmodified
* Software as contributed to or provided by such licensor, or (ii) the Larger
* Works (as defined below), to deal in both
*
*
* (a) the Software, and
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software (each a "Larger Work" to which the Software
* is contributed by such licensors),
*
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
*
* This license is subject to the following condition:
* The above copyright notice and either this complete permission notice or at
* a minimum a reference to the UPL must be included in all copies or
* substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -38,7 +38,7 @@
import { from, Observable, ObservableInput, of, ReplaySubject, forkJoin, throwError } from 'rxjs';
import { fromFetch } from 'rxjs/fetch';
import { catchError, combineLatest, concatMap, first, flatMap, map, tap } from 'rxjs/operators';
import { TargetService } from './Target.service';
import { Target, TargetService } from './Target.service';
import { Notifications } from '@app/Notifications/Notifications';

type ApiVersion = "v1" | "v2";
Expand Down Expand Up @@ -150,38 +150,68 @@ export class ApiService {
);
}

createTarget(target: Target): Observable<boolean> {
const form = new window.FormData();
form.append('connectUrl', target.connectUrl);
if (!!target.alias && !!target.alias.trim()) {
form.append('alias', target.alias);
}
return this.sendRequest(
'v2', `targets`,
{
method: 'POST',
body: form,
}
).pipe(
map(resp => resp.ok),
first(),
);
}

deleteTarget(target: Target): Observable<boolean> {
return this.sendRequest(
'v2', `targets/${encodeURIComponent(target.connectUrl)}`,
{
method: 'DELETE',
}
).pipe(
map(resp => resp.ok),
first(),
);
}

createRecording(recordingAttributes: RecordingAttributes): Observable<boolean> {
const form = new window.FormData();
form.append('recordingName', recordingAttributes.name);
form.append('events', recordingAttributes.events);
if (!!recordingAttributes.duration && recordingAttributes.duration > 0) {
form.append('duration', String(recordingAttributes.duration));
const form = new window.FormData();
form.append('recordingName', recordingAttributes.name);
form.append('events', recordingAttributes.events);
if (!!recordingAttributes.duration && recordingAttributes.duration > 0) {
form.append('duration', String(recordingAttributes.duration));
}
if (!!recordingAttributes.options){
if (recordingAttributes.options.toDisk != null) {
form.append('toDisk', String(recordingAttributes.options.toDisk));
}
if (!!recordingAttributes.options){
if (recordingAttributes.options.toDisk != null) {
form.append('toDisk', String(recordingAttributes.options.toDisk));
}
if (!!recordingAttributes.options.maxAge && recordingAttributes.options.maxAge >= 0) {
form.append('maxAge', String(recordingAttributes.options.maxAge));
}
if (!!recordingAttributes.options.maxSize && recordingAttributes.options.maxSize >= 0) {
form.append('maxSize', String(recordingAttributes.options.maxSize));
}
if (!!recordingAttributes.options.maxAge && recordingAttributes.options.maxAge >= 0) {
form.append('maxAge', String(recordingAttributes.options.maxAge));
}
if (!!recordingAttributes.options.maxSize && recordingAttributes.options.maxSize >= 0) {
form.append('maxSize', String(recordingAttributes.options.maxSize));
}
}

return this.target.target().pipe(concatMap(target =>
this.sendRequest('v1', `targets/${encodeURIComponent(target.connectUrl)}/recordings`, {
method: 'POST',
body: form,
}).pipe(
tap(resp => {
if (resp.ok) {
this.notifications.success('Recording created');
}
}),
map(resp => resp.ok),
first(),
)));
return this.target.target().pipe(concatMap(target =>
this.sendRequest('v1', `targets/${encodeURIComponent(target.connectUrl)}/recordings`, {
method: 'POST',
body: form,
}).pipe(
tap(resp => {
if (resp.ok) {
this.notifications.success('Recording created');
}
}),
map(resp => resp.ok),
first(),
)));
}

createSnapshot(): Observable<boolean> {
Expand Down
14 changes: 7 additions & 7 deletions src/app/Shared/Services/Target.service.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
/*
* Copyright The Cryostat Authors
*
*
* The Universal Permissive License (UPL), Version 1.0
*
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or data
* (collectively the "Software"), free of charge and under any and all copyright
* rights in the Software, and any and all patent rights owned or freely
* licensable by each licensor hereunder covering either (i) the unmodified
* Software as contributed to or provided by such licensor, or (ii) the Larger
* Works (as defined below), to deal in both
*
*
* (a) the Software, and
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software (each a "Larger Work" to which the Software
* is contributed by such licensors),
*
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
*
* This license is subject to the following condition:
* The above copyright notice and either this complete permission notice or at
* a minimum a reference to the UPL must be included in all copies or
* substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -52,7 +52,7 @@ class TargetService {
private readonly _sslFailure: Subject<void> = new Subject();

setTarget(target: Target): void {
if (target === NO_TARGET || (!!target.alias && !!target.connectUrl)) {
if (target === NO_TARGET || !!target.connectUrl) {
this._target.next(target);
} else {
throw new Error("Malformed target");
Expand Down
108 changes: 108 additions & 0 deletions src/app/TargetSelect/CreateTargetModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright The Cryostat Authors
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or data
* (collectively the "Software"), free of charge and under any and all copyright
* rights in the Software, and any and all patent rights owned or freely
* licensable by each licensor hereunder covering either (i) the unmodified
* Software as contributed to or provided by such licensor, or (ii) the Larger
* Works (as defined below), to deal in both
*
* (a) the Software, and
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software (each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
* The above copyright notice and either this complete permission notice or at
* a minimum a reference to the UPL must be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { Target } from '@app/Shared/Services/Target.service';
import { ActionGroup, Button, ButtonType, Form, FormGroup, Modal, ModalVariant, TextInput } from '@patternfly/react-core';
import * as React from 'react';

export interface CreateTargetModalProps {
visible: boolean;
onSubmit: (target: Target) => void;
onDismiss: () => void;
}

export const CreateTargetModal: React.FunctionComponent<CreateTargetModalProps> = (props) => {
const [connectUrl, setConnectUrl] = React.useState('');
const [alias, setAlias] = React.useState('');

const createTarget = React.useCallback(() => {
props.onSubmit({ connectUrl, alias: alias.trim() || connectUrl });
setConnectUrl('');
setAlias('');
}, [props.onSubmit, connectUrl, alias]);

const handleKeyDown = React.useCallback((evt) => {
if (evt.key === 'Enter') {
createTarget();
}
}, [createTarget]);

return (<>
<Modal
isOpen={props.visible}
variant={ModalVariant.small}
showClose={true}
onClose={props.onDismiss}
title="Create Target"
description="Create a custom target connection"
>
<Form isHorizontal>
<FormGroup
label="Connection URL"
isRequired
fieldId="connect-url"
helperText="JMX Service URL"
>
<TextInput
value={connectUrl}
isRequired
type="text"
id="connect-url"
onChange={setConnectUrl}
onKeyDown={handleKeyDown}
/>
</FormGroup>
<FormGroup
label="Alias"
fieldId="alias"
helperText="Connection Nickname"
>
<TextInput
value={alias}
type="text"
id="alias"
onChange={setAlias}
onKeyDown={handleKeyDown}
/>
</FormGroup>
</Form>
<ActionGroup>
<Button variant="primary" type={ButtonType.submit} onClick={createTarget}>Create</Button>
</ActionGroup>
</Modal>
</>);
}
Loading

0 comments on commit 007b073

Please sign in to comment.