Skip to content

Commit

Permalink
Workload Entry expiry features [Rebased+Updated Work] (#106)
Browse files Browse the repository at this point in the history
* entry expiry features added

Signed-off-by: Mohammed Abdi <[email protected]>

* check date validity properly

Signed-off-by: Mohammed Abdi <[email protected]>

* nits

Signed-off-by: Mohammed Abdi <[email protected]>

Signed-off-by: Mohammed Abdi <[email protected]>
Co-authored-by: Mohammed Abdi <[email protected]>
  • Loading branch information
mamy-CS and Mohammed Abdi authored Jan 10, 2023
1 parent 2dc04d0 commit 800a075
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 19 deletions.
14 changes: 14 additions & 0 deletions tornjak-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tornjak-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"file-saver": "^2.0.5",
"jwt-decode": "^3.1.2",
"keycloak-js": "^19.0.1",
"moment": "^2.29.4",
"node-sass": "^6.0.1",
"prop-types": "^15.6.0",
"puppeteer": "^10.4.0",
Expand Down
24 changes: 9 additions & 15 deletions tornjak-frontend/src/components/entry-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
tornjakServerInfoUpdateFunc,
serverInfoUpdateFunc,
agentworkloadSelectorInfoFunc,
newEntriesUpdateFunc
newEntriesUpdateFunc,
} from 'redux/actions';
import {
EntriesList,
Expand All @@ -32,10 +32,13 @@ import {
WorkloadSelectorInfoLabels,
} from './types';
import { RootState } from 'redux/reducers';
import CreateEntryYaml from './entry-create-json';
import EntryExpiryFeatures from './entry-expiry-features';
import CreateEntryJson from './entry-create-json';
// import PropTypes from "prop-types"; // needed for testing will be removed on last pr

type CreateEntryProp = {
// entry expiry time
globalEntryExpiryTime: number,
// dispatches a payload for the server selected and has a return type of void
serverSelectedFunc: (globalServerSelected: string) => void,
// dispatches a payload for list of agents with their metadata info as an array of AgentListType and has a return type of void
Expand Down Expand Up @@ -610,7 +613,7 @@ class CreateEntry extends Component<CreateEntryProp, CreateEntryState> {
"selectors": selectorEntries,
"admin": this.state.adminFlag,
"ttl": this.state.ttl,
"expires_at": this.state.expiresAt,
"expires_at": this.props.globalEntryExpiryTime,
"downstream": this.state.downstream,
"federates_with": federatedWithList,
"dns_names": dnsNamesWithList,
Expand Down Expand Up @@ -724,7 +727,7 @@ class CreateEntry extends Component<CreateEntryProp, CreateEntryState> {
<AccordionItem
title={<h5>Upload New Entry/ Entries</h5>} open>
<div className="entry-form">
<CreateEntryYaml
<CreateEntryJson
ParentIdList={ParentIdList_noManualOption} />
<br></br>
{this.props.globalNewEntries.length === 0 &&
Expand Down Expand Up @@ -855,17 +858,7 @@ class CreateEntry extends Component<CreateEntryProp, CreateEntryState> {
/>
</div>
<div className="expiresAt-input" data-test="expiresAt-input">
<NumberInput
helperText="Entry expires at (seconds since Unix epoch)"
id="expiresAt-input"
invalidText="Number is not valid"
label="Expires At"
//max={100}
min={0}
step={1}
value={0}
onChange={this.onChangeExpiresAt}
/>
<EntryExpiryFeatures />
</div>
<div className="federates-with-input-field" data-test="federates-with-input-field">
<TextInput
Expand Down Expand Up @@ -921,6 +914,7 @@ const mapStateToProps = (state: RootState) => ({
globalSelectorInfo: state.servers.globalSelectorInfo,
globalAgentsList: state.agents.globalAgentsList,
globalEntriesList: state.entries.globalEntriesList,
globalEntryExpiryTime: state.entries.globalEntryExpiryTime,
globalNewEntries: state.entries.globalNewEntries,
globalServerInfo: state.servers.globalServerInfo,
globalTornjakServerInfo: state.servers.globalTornjakServerInfo,
Expand Down
231 changes: 231 additions & 0 deletions tornjak-frontend/src/components/entry-expiry-features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Dropdown, TextInput, NumberInput, InlineNotification } from 'carbon-components-react';
import TornjakApi from './tornjak-api-helpers';
import './style.css';
import SpiffeHelper from './spiffe-helper';
import {
entryExpiryUpdateFunc
} from 'redux/actions';
import { RootState } from 'redux/reducers';
var moment = require('moment');
const JSMaxSafeTime = 8640000000000 // In seconds - JS cannot represent times safely larger than this

type EntryExpiryProp = {
// dispatches a payload for the entry expiry time and has a return type of void
entryExpiryUpdateFunc: (globalEntryExpiryTime: number) => void,
}

type EntryExpiryState = {
expiresAt: number,
expiryOption: string,
expiryOptionList: String[],
expiryDate: string,
expiryTime: string,
expiryUnsafe: boolean,
expiryInvalid: boolean,
}

class EntryExpiryFeatures extends Component<EntryExpiryProp, EntryExpiryState> {
TornjakApi: TornjakApi;
SpiffeHelper: SpiffeHelper;
constructor(props: EntryExpiryProp) {
super(props);
this.TornjakApi = new TornjakApi(props);
this.SpiffeHelper = new SpiffeHelper(props);
this.onChangeExpiryOption = this.onChangeExpiryOption.bind(this);
this.expiryTimeUpdate = this.expiryTimeUpdate.bind(this);
this.onChangeExpiresAtSeconds = this.onChangeExpiresAtSeconds.bind(this);
this.isValidDate = this.isValidDate.bind(this);
this.updateValidDateAndTime = this.updateValidDateAndTime.bind(this);
this.onChangeExpiresAtTime = this.onChangeExpiresAtTime.bind(this);
this.onChangeExpiresAtDate = this.onChangeExpiresAtDate.bind(this);
this.state = {
expiryOption: "None",
expiryOptionList: ["None", "Seconds Since Epoch", "Date/Time"],
expiryDate: "1/1/2021",
expiryTime: "00:00",
expiresAt: 0,
expiryUnsafe: false,
expiryInvalid: false,
}
}

componentDidMount() {
}

componentDidUpdate(prevProps: EntryExpiryProp, prevState: EntryExpiryState) {
this.props.entryExpiryUpdateFunc(this.state.expiresAt);
}

onChangeExpiryOption(e: { selectedItem: any; }) {
this.setState({
expiresAt: 0,
expiryOption: e.selectedItem,
expiryUnsafe: false,
expiryInvalid: false
});
}

isValidExpiryTime(seconds: number) {
return seconds > 0 && seconds <= JSMaxSafeTime
}

expiryTimeUpdate(seconds: number) {
this.setState({
expiresAt: seconds,
expiryUnsafe: !this.isValidExpiryTime(seconds)
})
}

onChangeExpiresAtSeconds(e: any) {
var seconds = Number(e.imaginaryTarget.value)
this.expiryTimeUpdate(seconds)
}

isValidDate(d: Date) { // date is successfully translated in Javascript
return d instanceof Date && !isNaN(d.getTime());
}

updateValidDateAndTime(d: string, t: string) {
var mo = moment(d + ' ' + t, "MM/DD/YYYY hh:mm:ss", true)
var testDate = mo._d; //extract date
this.setState({ // should always reflect what the user sees
expiryDate: d,
expiryTime: t
})
if (this.isValidDate(testDate) && mo.isValid()) {
this.setState({
expiryInvalid: false,
})
var mstoSecConvFactor = 1000;
var seconds = Math.floor(testDate.getTime() / mstoSecConvFactor)
this.expiryTimeUpdate(seconds)
console.log(d, t, this.state.expiryDate, this.state.expiryTime)
return
}
this.setState({
expiryInvalid: true,
})
}

onChangeExpiresAtDate(e: { target: { value: string; }; }) {
this.updateValidDateAndTime(e.target.value, this.state.expiryTime)
}

onChangeExpiresAtTime(e: { target: { value: string; }; }) {
this.updateValidDateAndTime(this.state.expiryDate, e.target.value)
}


render() {
return (
<div>
<div className="expiry-drop-down">
<Dropdown
//aria-required="true"
//ariaLabel="expiry-drop-down"
id="expiry-drop-down"
items={this.state.expiryOptionList}
label="None"
defaultValue="None"
titleText="Entry Expiry"
helperText="Choose Entry Expiry Format"
onChange={this.onChangeExpiryOption}
/>
</div>
{this.state.expiryOption !== "None" && <div className="expiryEntryFields">
{this.state.expiryOption === "Seconds Since Epoch" &&
<div className="expiryOption-field">
<div className="expiryOption-entry">
<NumberInput
aria-required="true"
helperText="(seconds since Unix epoch)"
id="expiresAt-input"
invalidText="Number is not valid"
label="Enter expiry time [*required]"
min={1}
step={1}
onChange={this.onChangeExpiresAtSeconds}
value={0} />
</div>
</div>
}
{this.state.expiryOption === "Date/Time" &&
<div className="expiryOption-field">
<div className="expiryOption-entry">
<TextInput
labelText="Enter expiry date [*required]"
helperText="mm/dd/yyyy or mm/dd/yyyyy"
placeholder="08/13/2345"
//pattern={["^\\d{2}/\\d{2}/\\d{4,5}$"]}
onChange={this.onChangeExpiresAtDate}
required id={''} />
</div>
<div className="expiryOption-entry">
<TextInput
labelText="Enter local time [*required]"
helperText="00:00:00 - 23:59:59 [hh:mm:ss]"
placeholder="hh:mm:ss"
//pattern={["^([0-1]\\d|2[0-3]):[0-5]\\d:[0-5]\\d$"]}
onChange={this.onChangeExpiresAtTime}
invalidText="NotGoodTime"
required id={''} />
</div>
</div>
}
</div>
}
{(this.state.expiryUnsafe || this.state.expiryInvalid) &&
<div>
<InlineNotification
kind="warning-alt"
hideCloseButton={true}
title="Warning"
subtitle={<span>Expiry time either in invalid format/ negative/ too large. Submitting this time may result in undefined behavior. </span>}
>
</InlineNotification>
{this.state.expiryOption === "Seconds Since Epoch" && this.state.expiryUnsafe &&
<InlineNotification
kind="warning-alt"
hideCloseButton={true}
title="Warning"
subtitle={<span>Seconds must be positive and less than MaxSafeTime="{JSMaxSafeTime} Seconds" </span>}
>
</InlineNotification>
}
{this.state.expiryOption === "Date/Time" && this.state.expiryUnsafe &&
<InlineNotification
kind="warning-alt"
hideCloseButton={true}
title="Warning"
subtitle={<span>Date must be past January 1, 1970 @ 12:00AM GMT" </span>}
>
</InlineNotification>
}
{this.state.expiryOption === "Date/Time" && this.state.expiryInvalid &&
<InlineNotification
kind="warning-alt"
hideCloseButton={true}
title="Warning"
subtitle={<span>Date or time format is invalid </span>}
>
</InlineNotification>
}
</div>
}
</div>
)
}
}


const mapStateToProps = (state: RootState) => ({
})

export default connect(
mapStateToProps,
{ entryExpiryUpdateFunc }
)(EntryExpiryFeatures)

export { EntryExpiryFeatures };
30 changes: 28 additions & 2 deletions tornjak-frontend/src/components/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,35 @@
}

.expiresAt-input {
display: inline-block;
margin-right: 20px;
margin-bottom: 20px;
width: 160px;
width: 600px;
}

.expiry-drop-down {
display: inline-block;
vertical-align: top;
margin-right: 20px;
width: 200px;
}

.expiryEntryFields {
display: inline-block;
vertical-align: top;
width: 200px;
}

.expiryOption-field {
display: inline-block;
vertical-align: top;
width: 500px;
}

.expiryOption-entry {
margin-right: 20px;
display: inline-block;
vertical-align: top;
width: 200px;
}

.federates-with-input-field {
Expand Down
Loading

0 comments on commit 800a075

Please sign in to comment.