-
Notifications
You must be signed in to change notification settings - Fork 640
Create/Edit page for Webhook secrets #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
frontend/package.json
Outdated
@@ -68,6 +68,7 @@ | |||
"plotly.js": "1.28.x", | |||
"prop-types": "15.6.x", | |||
"react": "16.x", | |||
"react-ace": "6.1.x", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're already using Ace editor elsewhere (edit YAML). I think we should be able to use Ace here without the new dependency. Can you check?
https://github.com/openshift/console/blob/master/frontend/public/components/edit-yaml.jsx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I see you comment on this in the description. I think we should discuss further. How large is the library?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from what I see in the node_modules it's 2MB. Just for comparision the brace
has 8.5MB
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't look at node_modules size. Use yarn run analyze
to see the size of the built js.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
react-ace is a thin wrapper around brace:
https://github.com/securingsincity/react-ace/blob/master/package.json#L63:
"dependencies": {
"brace": "^0.11.0",
...
},
https://github.com/securingsincity/react-ace/blob/master/src/ace.js#L1:
import ace from 'brace'
react-ace
itself is 46.47 KB. Previously we were async loading brace
only when needed. Since secret.jsx now imports brace
(747KB), our main bundle of vendored deps has increased in size by ~40%.
Are there any interactions here that aren't 90% copy/paste - ie, why do we need a text editor at all? If you do need one, you should use AsyncComponent
to load it only when needed since the vast majority of users will not create a secret.
cc @tlwu2013 |
Throwing out this multi-create button pattern that we have for OLM: What does @openshift/team-ux-review think of offloading the selection type to this button, which will reduce one of the fields on the screen, plus give you an option to jump to a raw YAML editor. Using a dropdown to switch this will alter the page a ton, plus you have no way to get back.
Also, I think we need some microcopy work here and none of those phrases mean anything to me:
|
Do you mean |
Not necessarily against using Opaque, but |
@jhadvig I'd suggest doing these one at a time in separate PRs, maybe starting with opaque secrets. I think we'll be able to get these reviewed and merged quicker that way. I don't know if the name/value editor will work as-is for generic secrets since they often have multiline values (e.g. certificates and keys). It would be good to structure this in such a way that we can use the same components for editing secrets, too. |
I think @robszumski's idea makes sense. Especially if that selection will drastically change the form underneath. As for terminology, sticking with the K8s standards is probably a good idea |
@spadgett I've update the PR based on the comments from @robszumski
When creating the secret Im adding all the values to the I've also added following actions for individual secrets:
PTAL |
e0f29ab
to
599edb8
Compare
@jhadvig the dropdown panel on the "Create New" button looks a bit misaligned. Looks great other than that |
@jhadvig Thanks this looks good, I just have a few comments:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to add complexity to list-page.jsx
when the use case is already handled. Also, you should update the description to accurately reflect the state of the PR.
case 'kubernetes.io/dockerconfigjson': | ||
case 'kubernetes.io/dockercfg': | ||
determinedType = 'image'; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just return
after each case
.
case 'source': | ||
case 'image': | ||
case 'generic': | ||
case 'webhook': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these all supposed to evaluate to WebHookSecretSubform
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No.. those are just subform options that console will eventually exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now the case
statements above will fall through to the 'webhook'
option. I would suggest YAGNI here and remove the switch
block altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, but the switch will be there once we add all the subforms options. Just wanted to have all the options on mind. But don't have any strong preferences regarding changing it to if
statement :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather leave out things we don't support yet in this PR.
|
||
const BindingLoadingWrapper = props => { | ||
const fixed = {}; | ||
_.each(props.fixedKeys, k => fixed[k] = _.get(props.obj.data, k)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const fixed = _.reduce(props.fixedKeys, (acc, k) => ({...acc, k: _.get(props.obj.data, k)}), {});
@@ -246,7 +246,19 @@ export const ListPage = props => { | |||
href = namespaced ? `/k8s/ns/${namespace || 'default'}/${ref}/new` : `/k8s/cluster/${ref}/new`; | |||
} catch (unused) { /**/ } | |||
} | |||
const createProps = createHandler ? {onClick: createHandler} : {to: href}; | |||
let createProps; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't necessary, it is already handled in the FireMan_
component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how the FireMan_ will evaluate the truly the Dropdown component, since the ListPage component sets createProps
either with onClick or to keys, there is no items key set, so there is no way Dropdown will be rendered. Or maybe I'm missing something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the implementation counts with setting the items
which is missing clearly in the ListPage component, since only onClick
and to
keys are set and therefor there is no way to create a Dropdown button. From what I can tell, those two implementations are used for different use-cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean. Try this:
- Allow passing
createProps
as a prop toListPage
- Change this line to:
const createProps = createProps || (createHandler ? {onClick: createHandler} : {to: href});
- Change
SecretsPage
to this:
const SecretsPage = props => {
const createItems = {
// source: 'Create Source Secret',
// image: 'Create Image Pull Secret',
// generic: 'Create Key/Value Secret',
webhook: 'Webhook Secret',
yaml: 'Secret from YAML',
};
const createProps = {
items: createItems,
createLink: (type) => `/k8s/ns/${props.obj.metadata.namespace}/secrets/${type === 'yaml' ? 'new' : type}`
};
return <ListPage ListComponent={SecretsList} canCreate={true} rowFilters={filters} createButtonText="Create" createProps={createProps} {...props} />;
};
I tried this locally and it works.
I understand the point but thought that options that will render an actual form should be first and the YAML editor should be the last one and by that somehow logically group them.
Sorry for the misleading screen, not sure why but when I took it it chopped off some pixeles from the left padding. The padding is the same on both sides - Regarding the issue @cshinn mentioned in his comment, this is an issue in the Dropdown component (it's also visible in #94 (comment)), which would require rework the component itself, so I would rather do it as a followup and open an issue for that one, if you don't mind. |
@jhadvig Got it, that makes sense to me on the order. |
frontend/public/components/app.jsx
Outdated
@@ -175,6 +176,13 @@ class App extends React.PureComponent { | |||
<Route path="/k8s/ns/:ns/roles/:name/:rule/edit" exact component={EditRulePage} /> | |||
<Route path="/k8s/ns/:ns/roles" exact component={rolesListPage} /> | |||
|
|||
<Route path="/k8s/cluster/secrets/new/:type" exact component={props => <CreateSecret {...props} kind="Secret" />} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can secrets be cluster scoped? I thought they had to be in a namespace
createProps = { | ||
items: createHandler, | ||
createLink(param) { | ||
return param === 'yaml' ? href : `${href}/${param}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd call encodeURIComponent
on param
to be safe
@@ -0,0 +1,4 @@ | |||
.separator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class doesn't look specific to secrets. It should probably go in core. Suggest adding the .co-
prefix, too.
Honestly, I would just remove the separator, though. I think it's better without it.
// source: 'Create Source Secret', | ||
// image: 'Create Image Pull Secret', | ||
// generic: 'Create Key/Value Secret', | ||
webhook: 'Create Webhook Secret', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only want to show the webhook secret option if this is an OpenShift cluster. You want connectToFlags(FLAGS.OPENSHIFT)
to check
Cog.factory.ModifyLabels, | ||
Cog.factory.ModifyAnnotations, | ||
(kind, obj) => ({ | ||
label: `Duplicate ${kind.label}...`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, is there a specific use case for copy that you had in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've seen that some of the sereources have the option to duplicate, but to be honest I wasnt sure if it should be available for secrets. Will put it away
stringData = secretsData; | ||
this.setState({stringData}); | ||
} | ||
changeSecretName (event) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest names onDataChanged
and onNameChanged
return Math.floor((1 + Math.random()) * 0x10000) | ||
.toString(16) | ||
.substring(1); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
import * as React from 'react'; | ||
|
||
const generateSecret = () => { | ||
//http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super nit here, but can you put a space before the http in the comment :)
<fieldset disabled={!this.props.isCreate}> | ||
<div className="form-group"> | ||
<label className="control-label">Secret Name</label> | ||
<div className="modal-body__field"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do the classes have modal-body
in them when they're not used in a modal?
e.preventDefault(); | ||
const { kind, metadata } = this.state.secret; | ||
this.setState({ inProgress: true }); | ||
const newSecretObject = _.assign(this.state.secret, {stringData: this.state.stringData}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mutates the original object, so is pretty much the same as
this.state.secret.stringData = this.state.stringData;
But I'd suggest
const { stringData } = this.state.stringData;
const newSecret = _.assign({}, this.state.secret, { stringData });
It looks like we need to update the integration tests for this change. |
@spadgett I've updated the PR:
Although I'm not sure why the tests are failing :-/ |
Ignore the ci/prow/* statuses. Linting is failing, though:
|
You can always use |
@spadgett sorry for that ... thought that interpolating |
@@ -85,7 +85,12 @@ describe('Kubernetes resource CRUD operations', () => { | |||
} | |||
|
|||
it('displays a YAML editor for creating a new resource instance', async() => { | |||
await crudView.createYAMLButton.click(); | |||
if (kind === 'Secret') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of checking kind
, can we check for the existence of #item-create
? Then if we add similar buttons to other pages, the tests don't need to change.
@@ -16,7 +16,22 @@ data: | |||
username: YWRtaW4= | |||
password: MWYyZDFlMmU2N2Rm`); | |||
|
|||
const menuActions = Cog.factory.common; | |||
const editInYaml = obj => { | |||
if (obj.type === 'Opaque' && _.has(obj.data, 'WebHookSecretKey') && Object.keys(obj.data).length === 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use
if (_.has(obj, 'data.WebHookSecretKey') && _.size(obj.data) === 1) {
The whole function could be simplified to
// Edit in YAML if not editing a webhook secret with one key.
const editInYaml = obj => !_.has(obj, 'data.WebHookSecretKey') || _size(obj.data) !== 1;
We should make the string WebHookSecretKey
a const defined once somewhere that we reuse to avoid typos.
secret: secret, | ||
inProgress: false, | ||
type: secret.type, | ||
stringData: _.mapValues(secret.data, (v) => window.atob(v)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be
stringData: _.mapValues(secret.data, window.atob),
type: secret.type, | ||
stringData: _.mapValues(secret.data, (v) => window.atob(v)), | ||
}; | ||
this.onNameChanged = this.onNameChanged.bind(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should bind onDataChanged
here, too
e.preventDefault(); | ||
const { kind, metadata } = this.state.secret; | ||
this.setState({ inProgress: true }); | ||
const newSecret = _.assign({}, this.state.secret, {stringData: this.state.stringData}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this include both data
and stringData
? We should only include stringData
const title = `${this.props.titleVerb} ${_.upperFirst(this.state.secretType)} Secret`; | ||
const { saveButtonText } = this.props; | ||
|
||
let explanation = 'Webhook secret allow you to authenticate a webhook trigger.'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'Webhook secrets allow you to authenticate a webhook trigger.'
const { saveButtonText } = this.props; | ||
|
||
let explanation = 'Webhook secret allow you to authenticate a webhook trigger.'; | ||
let subform = <WebHookSecretSubform onChange={this.onDataChanged.bind(this)} stringData={this.state.stringData} />; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if the base edit component didn't need to know about the specific secret types. I wonder if we can't use a higher-order component here:
https://reactjs.org/docs/higher-order-components.html#use-hocs-for-cross-cutting-concerns
|
||
const BaseEditSecret = connect(null, {setActiveNamespace: UIActions.setActiveNamespace})( | ||
(props: BaseEditSecretProps_) => <BaseEditSecret_ {...props} /> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this just be...
const BaseEditSecret = connect(null, {setActiveNamespace: UIActions.setActiveNamespace})(BaseEditSecret_);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No the props
need to be passed explicitly from parent to child otherwise, yarn will scream that some properties does not exist:
Property 'fixed' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<any, never>, ComponentState, never>...'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I think we must have declared the types incorrectly somehow. We should figure out what's wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that the redux part of the code is in jsx an therefor is not typed, eg. ui-actions.js. I think until we type the redux part of the then codebase, which means rewriting it to TS, until then we need this as a workaround.
Was trying this workaround but due to issue described above it havent worked.
const generateSecret = () => { | ||
// http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript | ||
const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); | ||
return s4()+s4()+s4()+s4(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spaces before and after +
{props.errorMessage && <ErrorMessage message={props.errorMessage} />} | ||
{injectDisabled(props.children, props.inProgress)} | ||
{props.inProgress && <LoadingInline />} | ||
{props.infoMessage && <InfoMessage message={props.infoMessage} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me why this change was needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was an issue with the required properties for the ButtonBar
TS2322: Type '{ children: Element[]; errorMessage: any; inProgress: boolean; }' is not assignable to type 'IntrinsicAttributes & { children: any; className: any; errorMessage: any; infoMessage: any; inPro...'.
Type '{ children: Element[]; errorMessage: any; inProgress: boolean; }' is not assignable to type '{ children: any; className: any; errorMessage: any; infoMessage: any; inProgress: any; }'.
Property 'className' is missing in type '{ children: Element[]; errorMessage: any; inProgress: boolean; }'.
Version: typescript 2.7.2
Time: 12738ms
It was cause by the listed params in the class definition
export const ButtonBar = ({children, className, errorMessage, infoMessage, inProgress}) => {
...
}
Because of listing the parameters and not just setting the to props
, all the listed params will be required, even if propTypes
will declare that they are not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you try adding an @type
comment instead and mark className option?
|
||
const determineSecretTypeAbstraction = (secret) => { | ||
const { data } = secret; | ||
if (_.has(data, 'WebHookSecretKey')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit, but I think you will see something like:
return _.has(data, 'WebHookSecretKey')
? 'webhook'
: 'generic';
Throughout the app. If possible I think we should try to conform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few more minor tweaks!
class BaseEditSecret_ extends SafetyFirst<BaseEditSecretProps_, BaseEditSecretState_> { | ||
constructor (props) { | ||
super(props); | ||
let existingData = _.pick(props.obj, ['metadata', 'data']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can const existingData
. It isn't changing.
this.save = this.save.bind(this); | ||
} | ||
onDataChanged (secretsData) { | ||
let stringData = {...this.state.stringData}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, you are defining stringData
then immediately setting it as something else.
Could this just be:
this.setState({... secretsData});
this.setState({stringData}); | ||
} | ||
onNameChanged (event) { | ||
let secret = {...this.state.secret}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
es6:
this.setState({
...this.state.secret,
metadata: {
name: event.target.value
}
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That will just set the name
in the metadata
field, everything else will be removed when set. Tried:
this.setState({
...this.state.secret,
metadata: {
name: event.target.value
...this.state.secret.namespace,
}
});
but thats not setting the name
at all
(this.props.isCreate | ||
? k8sCreate(ko, newSecret) | ||
: k8sUpdate(ko, newSecret, metadata.namespace, newSecret.metadata.name) | ||
).then( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind collapsing some parens? Ex: .then(() => {
.
</Firehose>; | ||
|
||
/* eslint-disable no-undef */ | ||
export type BaseEditSecretState_ = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the trailing underscore common? I haven't seen previously.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, at least we use it in our codebase
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, ok neat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not typically for the types, though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not. If anything we could rename:
BaseEditSecret
->BaseEditSecretWrapper
BaseEditSecret_
->BaseEditSecret
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the names are good. Just wondering if we should have the underscore on the type definitions
import { SafetyFirst } from '../safety-first'; | ||
import { WebHookSecretSubform } from './subforms'; | ||
|
||
const determineSecretTypeAbstraction = (secret) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be fine to name this determineSecretType()
or just secretType()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Secrets are a kind already have type
field, and all the values that could return from that function will be just an type abstractions:
- webhook
- source
- image
- key/value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok makes sense.
kind?: string, | ||
isCreate: boolean, | ||
titleVerb: string, | ||
setActiveNamespace: any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setActiveNamespace
must be a function, correct? I believe this can be typed.
error?: any, | ||
}; | ||
|
||
export type BaseEditSecretProps_ = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we eliminate the any
types?
For example:
obj: K8sResourceKind
// ...
metadata: {
name: string,
// etc.
}
I think this file has some better type arbitrary examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
can really be any... It's just saying always use these properties. I don't think we can define that one.
We can use K8sResourceKind for obj
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we eliminate the any types?
no all the types have to be typed, otherwise yarn will screen, eg:
Property 'metadata' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<BaseEditSecretProps_, "fixed" | "ki...'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since some parts of codebase are not typed the its almost impossible not to use any
const title = `${this.props.titleVerb} ${_.upperFirst(this.state.secretType)} Secret`; | ||
const { saveButtonText } = this.props; | ||
|
||
let explanation = 'Webhook secret allow you to authenticate a webhook trigger.'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const explanation;
const subform
;
/test backend |
@spadgett @benjaminapetersen comments addressed. |
OK to leave for now, but it's something that we might explore before adding all of the dialogs when it's harder to change. |
await crudView.createYAMLButton.click(); | ||
const exists = await crudView.createItemButton.isPresent(); | ||
if (exists) { | ||
console.log('true'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove console.log here and just below
import { connect } from 'react-redux'; | ||
import { Link } from 'react-router-dom'; | ||
|
||
import { WEBHOOKSECRETKEY } from '../../const'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably just export it in this file.
export const WebHookSecretKey = 'WebHookSecretKey';
@@ -0,0 +1,49 @@ | |||
import * as React from 'react'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These components are going to be so tightly coupled to create secret, I would keep them in the same file. Then you don't need to export them since they're internal to the implementation.
export class WebHookSecretSubform extends React.Component<WebHookSecretSubformProps, WebHookSecretSubformState> { | ||
constructor(props) { | ||
super(props); | ||
this.state = {WebHookSecretKey: this.props.stringData.WebHookSecretKey || ''}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd call this webhookSecretKey
. State properties usually begin with lowercase letters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true but this key is kinda specific, since it the only one from the known secret keys that starts with uppercase. Also the this.state
of WebHookSecretSubform is mapped to this.state.secret.stringData
of BaseEditSecret_, therefor we would need to handle the name conversion somewhere else eventually.
@@ -21,6 +21,7 @@ const InfoMessage = ({message}) => <div className="alert alert-info"><span class | |||
// NOTE: DO NOT use <a> elements within a ButtonBar. | |||
// They don't support the disabled attribute, and therefore | |||
// can't be disabled during a pending promise/request. | |||
/** @type {React.SFC<{children: any, className?: string, errorMessage?: string, infoMessage?: string, inProgress: boolean}}>} */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
const BaseEditSecret = connect(null, {setActiveNamespace: UIActions.setActiveNamespace})( | ||
(props: BaseEditSecretProps_) => <BaseEditSecret_ {...props} /> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I think we must have declared the types incorrectly somehow. We should figure out what's wrong.
</Firehose>; | ||
|
||
/* eslint-disable no-undef */ | ||
export type BaseEditSecretState_ = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not typically for the types, though?
|
||
/* eslint-disable no-undef */ | ||
export type BaseEditSecretState_ = { | ||
secretType?: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be better as an enum
@jhadvig Do you mind adding a screenshot of the latest? Thanks! |
@beanh66 there haven't been much of a changes, just wording in the form: |
@jhadvig Thanks! Question...should the last screen say "Edit Secret" as the title? Wondering why we need Generic. |
@beanh66 sorry for that, accidentally d'n'd old screen there.. the title should reflect the secret type, in this case it should be - |
@spadgett comments addressed and comments added. PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Thanks for your patience, @jhadvig. LGTM
/lgtm I suspect the ci/prow/* jobs are broken again, but let's check |
@jhadvig: The following tests failed, say
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
Thanks @spadgett much appreciated ! :) |
…w_shared Migration: Add shared components
Added button with
Create New
dropdown button, which (for now, rest will be added in a follow up) contains:When creating the secret Im adding all the values to the
stringData
which will take care of encoding the data.I've also added following actions for individual secrets:
PTAL