Skip to content
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

THREESCALE-2286: Refactor Service Discovery into a React component #763

Merged
merged 7 commits into from
May 29, 2019

Conversation

damianpm
Copy link
Contributor

@damianpm damianpm commented Apr 15, 2019

What this PR does / why we need it:

Refactor Service discovery into a React component
Add flow type checking
Add js tests

Which issue(s) this PR fixes

https://issues.jboss.org/browse/THREESCALE-2286

Verification steps

  • Configure a Service to be discovered on Openshift without SSO (instructions here).
  • Go to Dashboard
  • Click on +New Api
  • Click on Import from OpenShift

Screenshots:

New service:
manual


Service Discovery:
discovery


Error:
error

Note:
Please test in IE11 and Firefox <61

@codecov
Copy link

codecov bot commented Apr 17, 2019

Codecov Report

Merging #763 into master will increase coverage by <.01%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #763      +/-   ##
=========================================
+ Coverage    92.9%   92.9%   +<.01%     
=========================================
  Files        2414    2414              
  Lines       78366   78366              
=========================================
+ Hits        72806   72809       +3     
+ Misses       5560    5557       -3
Impacted Files Coverage Δ
lib/tasks/multitenant/tenants.rake 76.92% <0%> (+23.07%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 99ec8db...53fc8e7. Read the comment docs.

@damianpm damianpm force-pushed the THREESCALE-2286-Refactoring-Service-Discovery branch from 406b073 to a12ea99 Compare April 17, 2019 15:26
@damianpm damianpm force-pushed the THREESCALE-2286-Refactoring-Service-Discovery branch 4 times, most recently from b72b5a0 to 7dfa8c3 Compare April 19, 2019 10:05
@damianpm damianpm force-pushed the THREESCALE-2286-Refactoring-Service-Discovery branch 5 times, most recently from 77b3d2f to 53ba8a0 Compare April 30, 2019 12:47
@damianpm damianpm changed the title [WIP]THREESCALE-2286: Refactor Service Discovery into a React component THREESCALE-2286: Refactor Service Discovery into a React component Apr 30, 2019
@@ -0,0 +1,2 @@
import 'babel-polyfill'
import 'whatwg-fetch'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to make fetch and promises work in IE11.
I've found (in this GH Issue) this is the recommended way to do it when using webpacker.
Do you know a better way @didierofrivia @josemigallas ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've used it differently in the past: https://github.com/3scale/porta/blob/master/app/javascript/src/Stats/inlinechart/index.jsx#L6. However, if this new one is a preferred way by webpackers, I'd just keep it consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will keep it consistent with inlinechart component. Later, when we update webpacker gem, we can change it if we decide that.


const ServiceDiscoveryForm = (props: ServiceDiscoveryFormType) => {
const {isVisible, providerAdminServiceDiscoveryServicesPath, namespaces, services, selectedNamespace, handleChangeNamespaces} = props
return (<form className={`formtastic ${isVisible ? '' : 'is-hidden'}`} id="service_discovery" action={providerAdminServiceDiscoveryServicesPath} acceptCharset="UTF-8" method="post">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, we don't have a generic form component for React.
But as we are close to implement PF4-React and it includes all kind of form elements, I think we should keep this form (to match the styles of other forms generated by rails), and later replace it with PF4 forms
@josemigallas @didierofrivia @thomasmaas


document.addEventListener('DOMContentLoaded', () => {
const newServiceWrapper = document.getElementById('new_service_wrapper')
const newServiceData = JSON.parse(newServiceWrapper.dataset.newServiceData)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could use our json_utils here. Maybe decide what to do when the config is wrong...

import React from 'react'
// TODO: Get authenticityToken from rails
// Is this needed, or was just generated by formtastic ?
const authenticityToken = ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const renderSelectOptions = (options: string[]) => {
return options.length > 0
? options.map(option => <option key={option} value={option}>{option}</option>)
: undefined
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any value in returning explicitly undefined ?


import React from 'react'
// TODO: Get authenticityToken from rails
const authenticityToken = ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented above regarding getting this. You could extract that function to utils

}
render(<NewServiceWrapper {...props}/>, newServiceWrapper)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no component to tell the user there's no service data?

return response.json()
})
.then(data => data[dataItem].reduce((acc, current) => acc.concat(current.metadata.name), []))
.catch(error => console.error(error))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are catching the error that you are throwing 5 lines above, and using just the console. It's this meant to be like this? No message to the user?

}

// eslint-disable max-lines-per-function
const NewServiceWrapper = (props: Props) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could break this huge fn into smaller ones, and use composition and keeping the state separated.


const ServiceDiscoveryForm = (props: ServiceDiscoveryFormType) => {
const {isVisible, providerAdminServiceDiscoveryServicesPath, namespaces, services, selectedNamespace, handleChangeNamespaces} = props
return (<form className={`formtastic ${isVisible ? '' : 'is-hidden'}`} id="service_discovery" action={providerAdminServiceDiscoveryServicesPath} acceptCharset="UTF-8" method="post">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are using the same form here and in ServiceNewForm, you might want to extract that one in a separated component

handleChangeNamespaces: jest.fn()
}

it('should render properly ', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should render properly, I'm expecting some specifics not only the shallow component exists

@damianpm
Copy link
Contributor Author

There is still not enough space between radio button and label text I think.

@thomasmaas using 0.75 rems of space
input-space

@3scale 3scale deleted a comment from didierofrivia May 21, 2019
@3scale 3scale deleted a comment from didierofrivia May 21, 2019
Copy link
Contributor

@josemigallas josemigallas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from the comments, I think there are many meaningless specs. We should start focusing a bit more on that, we are adding a lot of new logic and components that are not tested and makes it less maintainable.


document.addEventListener('DOMContentLoaded', () => {
const newServiceWrapper = document.getElementById('new_service_wrapper')
const newServiceData = safeFromJsonString(newServiceWrapper.dataset.newServiceData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be called newServiceFormProps to be more explicit.

import React from 'react'

const ErrorMessage = ({error}: {error: string}) => {
const classNames = `errorMessage ${error ? '' : 'is-hidden'}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proper way to prevent a component from rendering is to return null not using css.

const ErrorMessage = ({error}: {error: string}) => {
const classNames = `errorMessage ${error ? '' : 'is-hidden'}`
return <p className={classNames}>
{`Sorry, your request has failed with the error: ${error}`}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a request? Is it an API error message? Why not simply rendering error?

Copy link
Contributor Author

@damianpm damianpm May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error comes from the request response, so yes.
What error do you think is more clear to the user @thomasmaas ?
We have something like this:
Sorry, your request has failed with the error: Cannot read property 'projects' of undefined

Copy link
Contributor

@josemigallas josemigallas May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error comes from the request response, so yes.

Ok, in that case it could be renamed FetchErrorMessage or NetworkErrorMessage

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@damianpm in order to write something in 'normal' english i first need to know what the error represents.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thomasmaas It will be a fetch error:
Cannot read property 'projects' of undefined
or
Cannot read property 'services' of undefined
Additionally, the browser shows a full error message like:
error-message


Enzyme.configure({adapter: new Adapter()})

const Props = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PascalCase is reserved for classes, component, types... Use better camel or UPPER.

const wrapper = shallow(<ServiceDiscoveryForm {...Props}/>)
expect(wrapper.find('#service_source').exists()).toEqual(true)
})
it('should render error message hidden by default', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What means 'by default'?

}

describe('Service Discovery Form', () => {
it('should render properly', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's 'properly'?


Enzyme.configure({adapter: new Adapter()})

describe('Service Source Form', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're including a describe block by default, perhaps it would help to use a more descriptive name for the specs. ServiceSourceForm doesn't mean much itself.

@damianpm damianpm force-pushed the THREESCALE-2286-Refactoring-Service-Discovery branch from d4eeda8 to bede262 Compare May 21, 2019 14:43
@damianpm damianpm force-pushed the THREESCALE-2286-Refactoring-Service-Discovery branch 4 times, most recently from d74726a to 4298083 Compare May 22, 2019 13:02
Copy link
Member

@didierofrivia didierofrivia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep it up Damian! thanks for the hard work, we are nearly the end now :)
ps: you might want to review the tests also.

)
} catch (error) {
console.error(error)
return null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, as my previous comment, you could instead of returning null, could return the input with the value null. That way you'll be addressing the type signature implicit of the function.
On a different matter, when @josemigallas suggested not to explicitly return the type of a React.Component I was fearing this case... since it would have failed the type check otherwise ;)

@damianpm damianpm force-pushed the THREESCALE-2286-Refactoring-Service-Discovery branch 2 times, most recently from 4dd1aab to 2071204 Compare May 22, 2019 15:17
@damianpm damianpm force-pushed the THREESCALE-2286-Refactoring-Service-Discovery branch from 2071204 to 628b9c5 Compare May 22, 2019 15:52
expect(wrapper.find('#service_discovery').exists()).toEqual(true)
})

it('should render `Import from OpenShift` input not disabled when `isServiceDiscoveryUsable` is true', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two things here:

  1. The description of the test should be more "human", since it's meant to communicate what you will assert. This one looks more like pseudo code to me. You could go with something like it('should render "Import from OS" input enabled when service discovery is selected', () =>...
  2. I'd expect to see the text "Import from OpenShift" and that property isServiceDiscoveryUsable asserted since you are mentioning them in the description, but they are not... These apparently are, as I understand, not important and end up being a bit misleading (In this case)

This applies to the rest of the tests below.

Copy link
Member

@didierofrivia didierofrivia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good! Don't forget to deploy first to OS and confirm it works as expected. Thanks for the hard work! 🎖

@damianpm damianpm merged commit 33a6ce8 into master May 29, 2019
@hallelujah hallelujah deleted the THREESCALE-2286-Refactoring-Service-Discovery branch February 28, 2020 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants