SendMessage.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import JSONEditor from 'components/common/JSONEditor/JSONEditor';
  2. import PageLoader from 'components/common/PageLoader/PageLoader';
  3. import {
  4. CreateTopicMessage,
  5. Partition,
  6. TopicMessageSchema,
  7. } from 'generated-sources';
  8. import React from 'react';
  9. import { useForm, Controller } from 'react-hook-form';
  10. import { useHistory } from 'react-router';
  11. import { clusterTopicMessagesPath } from 'lib/paths';
  12. import jsf from 'json-schema-faker';
  13. import validateMessage from './validateMessage';
  14. export interface Props {
  15. clusterName: string;
  16. topicName: string;
  17. fetchTopicMessageSchema: (clusterName: string, topicName: string) => void;
  18. sendTopicMessage: (
  19. clusterName: string,
  20. topicName: string,
  21. payload: CreateTopicMessage
  22. ) => void;
  23. messageSchema: TopicMessageSchema | undefined;
  24. schemaIsFetched: boolean;
  25. messageIsSending: boolean;
  26. partitions: Partition[];
  27. }
  28. const SendMessage: React.FC<Props> = ({
  29. clusterName,
  30. topicName,
  31. fetchTopicMessageSchema,
  32. sendTopicMessage,
  33. messageSchema,
  34. schemaIsFetched,
  35. messageIsSending,
  36. partitions,
  37. }) => {
  38. const [keyExampleValue, setKeyExampleValue] = React.useState('');
  39. const [contentExampleValue, setContentExampleValue] = React.useState('');
  40. const [schemaIsReady, setSchemaIsReady] = React.useState(false);
  41. const [schemaErrors, setSchemaErrors] = React.useState<string[]>([]);
  42. const {
  43. register,
  44. handleSubmit,
  45. formState: { isSubmitting, isDirty },
  46. control,
  47. } = useForm({ mode: 'onChange' });
  48. const history = useHistory();
  49. jsf.option('fillProperties', false);
  50. jsf.option('alwaysFakeOptionals', true);
  51. React.useEffect(() => {
  52. fetchTopicMessageSchema(clusterName, topicName);
  53. }, []);
  54. React.useEffect(() => {
  55. if (schemaIsFetched && messageSchema) {
  56. setKeyExampleValue(
  57. JSON.stringify(
  58. jsf.generate(JSON.parse(messageSchema.key.schema)),
  59. null,
  60. '\t'
  61. )
  62. );
  63. setContentExampleValue(
  64. JSON.stringify(
  65. jsf.generate(JSON.parse(messageSchema.value.schema)),
  66. null,
  67. '\t'
  68. )
  69. );
  70. setSchemaIsReady(true);
  71. }
  72. }, [schemaIsFetched]);
  73. const onSubmit = async (data: {
  74. key: string;
  75. content: string;
  76. headers: string;
  77. partition: number;
  78. }) => {
  79. if (messageSchema) {
  80. const key = data.key || keyExampleValue;
  81. const content = data.content || contentExampleValue;
  82. const { partition } = data;
  83. const headers = data.headers ? JSON.parse(data.headers) : undefined;
  84. const messageIsValid = await validateMessage(
  85. key,
  86. content,
  87. messageSchema,
  88. setSchemaErrors
  89. );
  90. if (messageIsValid) {
  91. sendTopicMessage(clusterName, topicName, {
  92. key,
  93. content,
  94. headers,
  95. partition,
  96. });
  97. history.push(clusterTopicMessagesPath(clusterName, topicName));
  98. }
  99. }
  100. };
  101. if (!schemaIsReady) {
  102. return <PageLoader />;
  103. }
  104. return (
  105. <div className="box">
  106. <form onSubmit={handleSubmit(onSubmit)}>
  107. <div className="columns">
  108. <div className="column is-one-third">
  109. <label className="label" htmlFor="select">
  110. Partition
  111. </label>
  112. <div className="select is-block">
  113. <select
  114. id="select"
  115. defaultValue={partitions[0].partition}
  116. disabled={isSubmitting || messageIsSending}
  117. {...register('partition')}
  118. >
  119. {partitions.map((partition) => (
  120. <option key={partition.partition} value={partition.partition}>
  121. {partition.partition}
  122. </option>
  123. ))}
  124. </select>
  125. </div>
  126. </div>
  127. </div>
  128. <div className="columns">
  129. <div className="column is-one-half">
  130. <label className="label">Key</label>
  131. <Controller
  132. control={control}
  133. name="key"
  134. render={({ field: { name, onChange } }) => (
  135. <JSONEditor
  136. readOnly={isSubmitting || messageIsSending}
  137. defaultValue={keyExampleValue}
  138. name={name}
  139. onChange={onChange}
  140. />
  141. )}
  142. />
  143. </div>
  144. <div className="column is-one-half">
  145. <label className="label">Content</label>
  146. <Controller
  147. control={control}
  148. name="content"
  149. render={({ field: { name, onChange } }) => (
  150. <JSONEditor
  151. readOnly={isSubmitting || messageIsSending}
  152. defaultValue={contentExampleValue}
  153. name={name}
  154. onChange={onChange}
  155. />
  156. )}
  157. />
  158. </div>
  159. </div>
  160. <div className="columns">
  161. <div className="column">
  162. <label className="label">Headers</label>
  163. <Controller
  164. control={control}
  165. name="headers"
  166. render={({ field: { name, onChange } }) => (
  167. <JSONEditor
  168. readOnly={isSubmitting || messageIsSending}
  169. defaultValue="{}"
  170. name={name}
  171. onChange={onChange}
  172. height="200px"
  173. />
  174. )}
  175. />
  176. </div>
  177. </div>
  178. {schemaErrors && (
  179. <div className="mb-4">
  180. {schemaErrors.map((err) => (
  181. <p className="help is-danger" key={err}>
  182. {err}
  183. </p>
  184. ))}
  185. </div>
  186. )}
  187. <button
  188. type="submit"
  189. className="button is-primary"
  190. disabled={!isDirty || isSubmitting || messageIsSending}
  191. >
  192. Send
  193. </button>
  194. </form>
  195. </div>
  196. );
  197. };
  198. export default SendMessage;