Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

feat: Add useTopics model hook and query for topics listing #136

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

ajborley
Copy link
Member

@ajborley ajborley commented Dec 2, 2020

  • This commit adds a new useTopics model hook, which makes GraphQL
    requests to the server to administer Kafka topics.
  • The hook returns a set of sub-hooks, with just the useGetTopics
    sub-hook implemented for now. useGetTopics returns the list of topics
    (including name, number of partitions and number of replicas).

Contributes to: #124

Signed-off-by: Andrew Borley [email protected]

@github-actions
Copy link

github-actions bot commented Dec 2, 2020

PR Report

Bundle Sizes

View bundle sizes for 'client'
Bundle New Size Original Size Increase/Decrease File
1.bundle.js 633.3KB 631.33KB 0% 1.bundle.js
Strimzi UI JS Bundle 12.09KB 11.08KB 9% main.bundle.js
Overall bundle size change: 0.47%
View bundle sizes for 'server'
Bundle New Size Original Size Increase/Decrease File
Strimzi UI Server JS Bundle 26.71KB 26.67KB 0% main.js
Overall bundle size change: 0.16%

Test Coverage

View test coverage
File Lines Statement Functions Branches
Total 100% 100% 100% 100%
client/Bootstrap/Navigation/useRouteConfig/useRouteConfig.hook.ts 100% 100% 100% 100%
client/Contexts/ConfigFeatureFlag/Context.tsx 100% 100% 100% 100%
client/Contexts/ConfigFeatureFlag/FeatureFlag.view.tsx 100% 100% 100% 100%
client/Contexts/Introspect/Introspection.ts 100% 100% 100% 100%
client/Contexts/Logging/Context.tsx 100% 100% 100% 100%
client/Hooks/useConfigFeatureFlag/useConfigFeatureFlag.ts 100% 100% 100% 100%
client/Hooks/useLogger/Hook.ts 100% 100% 100% 100%
client/Panels/Home/Home.tsx 100% 100% 100% 100%
client/Panels/Topics/Model.ts 100% 100% 100% 100%
client/Panels/Topics/View.carbon.tsx 100% 100% 100% 100%
client/Utils/sanitise/sanitise.ts 100% 100% 100% 100%
client/Utils/window/window.ts 100% 100% 100% 100%
File Lines Statement Functions Branches
Total 100% 100% 100% 100%
server/api/controller.ts 100% 100% 100% 100%
server/api/router.ts 100% 100% 100% 100%
server/client/controller.ts 100% 100% 100% 100%
server/client/router.ts 100% 100% 100% 100%
server/config/controller.ts 100% 100% 100% 100%
server/config/router.ts 100% 100% 100% 100%
server/core/app.ts 100% 100% 100% 100%
server/core/modules.ts 100% 100% 100% 100%
server/log/router.ts 100% 100% 100% 100%
server/mockapi/data.ts 100% 100% 100% 100%
server/mockapi/router.ts 100% 100% 100% 100%

Triggered by commit: a4c0f81

Copy link
Member

@matthew-chirgwin matthew-chirgwin left a comment

Choose a reason for hiding this comment

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

Thanks for this Andy!

The core code looks good - as per the doc however, the model hook should be private, and defined in the 'Topics' panel component (along with tests to match/align). I also think a few capabilities may be need adding to the hook too.

Other than that, this LGTM

@@ -0,0 +1,37 @@
# Models
Copy link
Member

Choose a reason for hiding this comment

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

The model for a Panel/Group is a hook, but really should be a private one - ie useTopics (I would suggest a rename to useTopicModel to be hyper picky) would be defined in a Model.ts file in the Topics Panel (and not exported as it is private/internals).

As for testing, Panels are meant to be behaviourally tested. Ie, a table with data appears with the expected info. As there is no UI (yet), thus no table, it makes asserting (indirectly) that the model is behaving abit more obtuse. I think what would be sensible is to add a placeholder View.patternfly.ts/View.carbon.ts, which just stringifies the model output. Then, tests can assert the right data is present (just not in a table), which can then be updated when the component work comes along

Copy link
Contributor

Choose a reason for hiding this comment

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

Just to confirm - models live inside the component that needs them, whereas reusable hooks go in the hooks dir?

Therefore, useTopics is a particular hook (if we want to encapsulate for reuse) - and the Model can either call useTopics or just the default useQuery?

Copy link
Member

@matthew-chirgwin matthew-chirgwin Dec 3, 2020

Choose a reason for hiding this comment

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

Just to confirm - models live inside the component that needs them, whereas reusable hooks go in the hooks dir?

Therefore, useTopics is a particular hook (if we want to encapsulate for reuse) - and the Model can either call useTopics or just the default useQuery?

Yes - absolutley. If the intent was to have a reusable hook representing/abstracting the query, than that should be in Hooks. Given the context (and other changes), I reviewed/interpreted it as a model hook - and that (as is), model hooks are defined with their components, not in their own section (so I am expecting the code to move, and this readme to be removed).

Copy link
Member Author

Choose a reason for hiding this comment

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

Rearranged/refactored the code into a private hook in the Topics panel component.

import { useTopicsType } from './useTopics.types';

export const useTopics = (): useTopicsType => {
const useGetTopics = () => useQuery(GET_TOPICS);
Copy link
Member

Choose a reason for hiding this comment

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

I would cross reference the behviours described here: #70 (comment)

I think we will need to also have a variable for topic name, which gets debounced etc

Copy link
Member

Choose a reason for hiding this comment

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

Should also add, the consumer of the model is the view. Within reason, if there are things the model can do to make the view as simple as possible (eg if no data returning an empty array), or obfiscate (loading, error states etc) that would be good too.

Copy link
Member Author

@ajborley ajborley Dec 4, 2020

Choose a reason for hiding this comment

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

Have added client-side filter handling for the topics listing. The mockapi server doesn't support this yet, so it currently doesn't make any difference when running the webpack-dev-server, but you can see the filter input field changes are debounced correctly by watching the network calls..

@matthew-chirgwin matthew-chirgwin linked an issue Dec 3, 2020 that may be closed by this pull request

## Test approach

Elements should be tested in a functional manor. See [Test Driven Development](../../docs/Test.md#style-of-test).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Elements should be tested in a functional manor. See [Test Driven Development](../../docs/Test.md#style-of-test).
Elements should be tested in a functional manner. See [Test Driven Development](../../docs/Test.md#style-of-test).

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed - along with all the others :)

return (
<div className='home'>
<img src={image} alt='Strimzi logo' />
<p>Welcome to the Strimzi UI</p>
{showVersion && isComplete && <p>{`Version: ${version}`}</p>}
{loading || !data ? (
<p>Loading...</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

Translation ;)

@@ -0,0 +1,37 @@
# Models
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to confirm - models live inside the component that needs them, whereas reusable hooks go in the hooks dir?

Therefore, useTopics is a particular hook (if we want to encapsulate for reuse) - and the Model can either call useTopics or just the default useQuery?

 - This commit adds a new `useTopics` model hook, which makes GraphQL
requests to the server to administer Kafka topics.
 - The hook returns a set of sub-hooks, with just the `useGetTopics`
sub-hook implemented for now. `useGetTopics` returns the list of topics
(including name, number of partitions and number of replicas).

Contributes to: strimzi#124

Signed-off-by: Andrew Borley <[email protected]>
 - Addresses initial review comments.

Contributes to: strimzi#124

Signed-off-by: Andrew Borley <[email protected]>
Copy link
Member

@matthew-chirgwin matthew-chirgwin left a comment

Choose a reason for hiding this comment

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

Couple of minor comments

@@ -52,7 +52,7 @@ When('it is rendered with no version', () => {
showVersionSet = false;
});

Then('it should display the expected text', () => {
Then('it should display the expected text', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

Wont block the PR on it, but was this intended?

fireEvent.change(renderResult.getByPlaceholderText('filter'), {
target: { value: filter },
});
await waitFor(() => {
Copy link
Member

Choose a reason for hiding this comment

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

My personal preference would be that rather than using mock timers, we use the apollo helpers, eg

await apolloMockResponse(); // tick for data

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed.

* Copyright Strimzi authors.
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
*/
import { generateMockDataResponseForGQLRequest } from 'utils/test/withApollo/withApollo.util';
Copy link
Member

Choose a reason for hiding this comment

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

import { generateMockDataResponseForGQLRequest } from 'utils/test' ?

describe('`useTopics` hook', () => {
describe('`useGetTopics` hook', () => {
beforeEach(() => {
jest.useFakeTimers();
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here re use of helper functions, and not to control timers

};

return {
useGetTopics,
Copy link
Member

Choose a reason for hiding this comment

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

This is interesting (and not what I expected - not to say that is wrong!). I expected that we would also be owning/exporting the state for the filter - ie its value and function to update it, which in fact is a (lodash) debounced version. When that debounce clears, then the value is set, firing the query. Does this mean the view is making use of a useState call to track the filter value?

Edit: having seen the code below, I see this is the case. My preference would be the Model does all hook work, and that the view imports as few hooks as possible (for easy sharing of the business logic/keeping the view layers thin)

@matthew-chirgwin matthew-chirgwin requested review from a team, pmuir and dlabaj December 7, 2020 10:32
 - Based on review comments from @matthew-chirgwin

Contributes to: strimzi#124

Signed-off-by: Andrew Borley <[email protected]>
@ajborley
Copy link
Member Author

ajborley commented Dec 7, 2020

@matthew-chirgwin - I have pushed up a new commit based on your suggestions. It seems to work fine in webpack-dev-server and the e2e server, and tests are passing, but I'm getting an eslint warning that I haven't been able to get past so far.


init(); //Bootstrap i18next support
ReactDOM.render(
<ApolloProvider client={apolloClient}>
<ConfigFeatureFlagProvider>
<LoggingProvider>
<FeatureFlag flag={'client.Pages.PlaceholderHome'}>
<Home />
<Home>
<Topics />
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs a router, not hard coding like this.

import debounce from 'lodash.debounce';

const onChangeEvent = (
evt: ChangeEvent<HTMLInputElement>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't use html events here, it breaks the MVVC abstraction. Use scalar types or add an abstraction.

debounce(setTopicsFilter, 500),
[]
);
const { data, loading, error } = useQuery<topicsType>(GET_TOPICS, {
Copy link
Contributor

Choose a reason for hiding this comment

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

Types start with capital letters.

fireEvent.change(renderResult.getByPlaceholderText('filter'), {
target: { value: filter },
});
await waitFor(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed.

name: string;
partitions: number;
replicas: number;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Use the types generated by the Rama's PR here.

replicas
}
}
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not valid graphql schema - update to latest schema from admin server.

Base automatically changed from master to main March 25, 2021 11:13
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement model & controller for Topics (listing) page
5 participants