Skip to content

Commit 510e3d8

Browse files
authored
Merge pull request acidonper#12 from acidonper/feature/grpc
Feature/grpc
2 parents f5cfdd9 + a4e60c2 commit 510e3d8

11 files changed

+736
-7
lines changed

README.md

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
# Getting Started with Create React App
1+
# Jump App Frontend based on React
22

3-
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
3+
This repository includes a microservice based on React and TypeScript that is a component develop for Jump App application. The idea of this microservice is implement a Frontend which will be able to send HTTP and gRPC request to a respective backend.
4+
5+
The idea of this connection to the backend is to send a JSON or Protobuf object in order to be able to make a set of "jumps" (connection between services) that have been implemented as microservices in different languages (Golang, Python, Java or Quarkus).
46

7+
Please review the following section for more information about this microservice.
58
## Available Scripts
69

10+
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
11+
712
In the project directory, you can run:
813

914
### `npm start`
@@ -39,6 +44,20 @@ Instead, it will copy all the configuration files and the transitive dependencie
3944

4045
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
4146

47+
## gRPC Support
48+
49+
In order to be able to test some features integrated in Istio (based on Envoy), gRPC support has been implemented. With this new feature, it is possible to sent gRPC request in the same way as HTTP requests.
50+
51+
It is important to bear in mind that a gRPC proxy (E.g. Envoy) is required to handle the gRPC requests and redirect them to the a specific backend. For this reason, the following command will be useful in local testing to make available this envoy service locally:
52+
53+
```$bash
54+
envoy -c local/envoy.yaml
55+
```
56+
57+
NOTE: It is required to install envoy previously. Visit [link](https://www.envoyproxy.io/docs/envoy/latest/start/install) for more information.
58+
59+
Please visit [gRPC Web](https://github.com/grpc/grpc-web) for more information about this integration.
60+
4261
## Learn More
4362

4463
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).

local/envoy.yaml

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
admin:
2+
access_log_path: /tmp/admin_access.log
3+
address:
4+
socket_address: { address: 0.0.0.0, port_value: 10001 }
5+
6+
static_resources:
7+
listeners:
8+
- name: listener_0
9+
enable_reuse_port: false
10+
address:
11+
socket_address: { address: 0.0.0.0, port_value: 10000 }
12+
filter_chains:
13+
- filters:
14+
- name: envoy.filters.network.http_connection_manager
15+
typed_config:
16+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
17+
codec_type: auto
18+
stat_prefix: ingress_http
19+
route_config:
20+
name: local_route
21+
virtual_hosts:
22+
- name: local_service
23+
domains: ["*"]
24+
routes:
25+
- match: { prefix: "/" }
26+
route:
27+
cluster: back_golang_service
28+
timeout: 0s
29+
max_stream_duration:
30+
grpc_timeout_header_max: 0s
31+
cors:
32+
allow_origin_string_match:
33+
- prefix: "*"
34+
allow_methods: GET, PUT, DELETE, POST, OPTIONS
35+
allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
36+
max_age: "1728000"
37+
expose_headers: custom-header-1,grpc-status,grpc-message
38+
http_filters:
39+
- name: envoy.filters.http.grpc_web
40+
- name: envoy.filters.http.cors
41+
- name: envoy.filters.http.router
42+
clusters:
43+
- name: back_golang_service
44+
connect_timeout: 0.25s
45+
type: logical_dns
46+
http2_protocol_options: {}
47+
lb_policy: round_robin
48+
load_assignment:
49+
cluster_name: cluster_0
50+
endpoints:
51+
- lb_endpoints:
52+
- endpoint:
53+
address:
54+
socket_address:
55+
address: golang.acidonpe.com
56+
port_value: 50051

package-lock.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"@types/react-dom": "^16.9.9",
1515
"axios": "^0.21.0",
1616
"classnames": "^2.2.6",
17+
"google-protobuf": "^3.19.1",
18+
"grpc-web": "^1.3.0",
1719
"install": "^0.13.0",
1820
"npm": "^6.14.10",
1921
"react": "^17.0.1",

src/infrastructure/postJump.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ export const sentJump = async (
2323
return response.data;
2424
} catch (error) {
2525
console.log(error);
26-
return error;
26+
return {code: 500, message: "/jump - Farewell from Backend -" + url};
2727
}
2828
};

src/infrastructure/postJumpGrpc.tsx

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as grpcWeb from 'grpc-web';
2+
import {JumpServiceClient} from '../utils/grpc/JumpServiceClientPb';
3+
import {JumpReq, Response} from '../utils/grpc/jump_pb';
4+
import { ResponseBack } from '../domain/ResponseBack';
5+
6+
export const sentJumpGrpc = async (
7+
url: string,
8+
dataJump: string[]
9+
): Promise <ResponseBack> => {
10+
return new Promise<ResponseBack>(function(resolve) {
11+
const jumpService = new JumpServiceClient(url, null, null);
12+
var finalResponse: Response.AsObject = {code: 500, message: "/jump - Farewell from Backend - " + url};
13+
14+
const request = new JumpReq();
15+
request.setMessage('Hello from React!');
16+
request.setCount(0)
17+
request.setJumpsList(dataJump)
18+
19+
const call = jumpService.jump(request, {'Content-Type': 'application/grpc'},
20+
(err: grpcWeb.RpcError, response: Response) => {
21+
try {
22+
console.log(response.getMessage());
23+
finalResponse.code = response.getCode();
24+
finalResponse.message = response.getMessage();
25+
resolve(finalResponse);
26+
} catch {
27+
console.log("empty Response - ", err)
28+
resolve(finalResponse);
29+
}
30+
});
31+
call.on('status', (status: grpcWeb.Status) => {
32+
if (status.metadata) {
33+
console.log('Received metadata');
34+
console.log(status);
35+
}
36+
});
37+
});
38+
}

src/ui/home/Home.module.css

+11-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,17 @@
7070
.jumps-box h1 {
7171
color: var(--on-secondary-color);
7272
padding-bottom: var(--m);
73-
font-size: var(--body-size-xl);
73+
font-size: var(--body-size-xxl);
74+
align-self: center;
75+
}
76+
77+
.jumps-box-grpc-section {
78+
padding-bottom: var(--l);
79+
display: flex;
80+
align-items: center;
81+
min-width: 25vh;
82+
flex-direction: column;
83+
font-size: var(--body-size-m);
7484
}
7585

7686
.jumps-box-item {

src/ui/home/Home.tsx

+27-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
22
import { bind } from '../../utils/bind';
33
import styles from './Home.module.css';
44
import { sentJump } from '../../infrastructure/postJump';
5+
import { sentJumpGrpc } from '../../infrastructure/postJumpGrpc';
56
import { ResponseBack } from '../../domain/ResponseBack';
67
import { AppService } from '../../domain/AppService';
78
import { JumpLog } from '../../domain/JumpLog';
@@ -52,6 +53,7 @@ export const Home: React.FunctionComponent<Props> = () => {
5253
};
5354

5455
const [data, setData] = useState(jumps);
56+
const [grpc, setGrpc] = useState(false);
5557
const [calls, setCalls] = useState(1);
5658
const [callsInterval, setCallsInterval] = useState(1);
5759
const [callLogs, setCallLogs] = useState({ ...jumpLogTest });
@@ -67,11 +69,13 @@ export const Home: React.FunctionComponent<Props> = () => {
6769
const sendJumps = async () => {
6870
setCallLogs({ ...jumpLogTest });
6971

72+
const jumpsRequest = data.map((i) => i.jump)
73+
7074
const finalJump: Jump = {
7175
message: 'hello',
7276
jump_path: '/jump',
7377
last_path: '/jump',
74-
jumps: data.map((i) => i.jump)
78+
jumps: jumpsRequest,
7579
};
7680

7781
const timeout = async (ms: number) => {
@@ -83,7 +87,14 @@ export const Home: React.FunctionComponent<Props> = () => {
8387
for (let index = 0; index < calls; index++) {
8488
await timeout(callsInterval);
8589
setCallLogs({ ...jumpLogTest });
86-
const jump: ResponseBack = await sentJump(appBack, finalJump);
90+
91+
var jump: ResponseBack = {} as any;
92+
93+
if (!grpc) {
94+
jump = await sentJump(appBack, finalJump);
95+
} else {
96+
jump = await sentJumpGrpc(appBack, jumpsRequest)
97+
}
8798
const timeLog = new Date();
8899
const time = timeLog.getTime();
89100
const date = new Date(time);
@@ -185,7 +196,20 @@ export const Home: React.FunctionComponent<Props> = () => {
185196
</div>
186197
</div>
187198
<div role='jumpbox' className={cx('jumps-box')}>
188-
<h1>Jumps</h1>
199+
<h1>Jumps</h1>
200+
<div className={cx('jumps-box-grpc-section')}>
201+
<p>
202+
<input
203+
type="checkbox"
204+
id="grpc"
205+
name="grpc"
206+
defaultChecked={grpc}
207+
onChange={(event) =>
208+
setGrpc(event.target.checked)
209+
}/>
210+
gRPC Enabled
211+
</p>
212+
</div>
189213
<DragDropContext onDragEnd={handleOnDragEnd}>
190214
<Droppable droppableId='jumps'>
191215
{(provided) => (

src/utils/grpc/JumpServiceClientPb.ts

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @fileoverview gRPC-Web generated client stub for jump
3+
* @enhanceable
4+
* @public
5+
*/
6+
7+
// GENERATED CODE -- DO NOT EDIT!
8+
9+
10+
/* eslint-disable */
11+
// @ts-nocheck
12+
import * as grpcWeb from 'grpc-web';
13+
14+
import * as jump_jump_pb from './jump_pb';
15+
16+
export class JumpServiceClient {
17+
client_: grpcWeb.AbstractClientBase;
18+
hostname_: string;
19+
credentials_: null | { [index: string]: string; };
20+
options_: null | { [index: string]: any; };
21+
22+
constructor (hostname: string,
23+
credentials?: null | { [index: string]: string; },
24+
options?: null | { [index: string]: any; }) {
25+
if (!options) options = {};
26+
if (!credentials) credentials = {};
27+
options['format'] = 'binary';
28+
29+
this.client_ = new grpcWeb.GrpcWebClientBase(options);
30+
this.hostname_ = hostname;
31+
this.credentials_ = credentials;
32+
this.options_ = options;
33+
}
34+
35+
methodInfoJump = new grpcWeb.MethodDescriptor(
36+
'/jump.JumpService/Jump',
37+
grpcWeb.MethodType.UNARY,
38+
jump_jump_pb.JumpReq,
39+
jump_jump_pb.Response,
40+
(request: jump_jump_pb.JumpReq) => {
41+
return request.serializeBinary();
42+
},
43+
jump_jump_pb.Response.deserializeBinary
44+
);
45+
46+
jump(
47+
request: jump_jump_pb.JumpReq,
48+
metadata: grpcWeb.Metadata | null): Promise<jump_jump_pb.Response>;
49+
50+
jump(
51+
request: jump_jump_pb.JumpReq,
52+
metadata: grpcWeb.Metadata | null,
53+
callback: (err: grpcWeb.RpcError,
54+
response: jump_jump_pb.Response) => void): grpcWeb.ClientReadableStream<jump_jump_pb.Response>;
55+
56+
jump(
57+
request: jump_jump_pb.JumpReq,
58+
metadata: grpcWeb.Metadata | null,
59+
callback?: (err: grpcWeb.RpcError,
60+
response: jump_jump_pb.Response) => void) {
61+
if (callback !== undefined) {
62+
return this.client_.rpcCall(
63+
this.hostname_ +
64+
'/jump.JumpService/Jump',
65+
request,
66+
metadata || {},
67+
this.methodInfoJump,
68+
callback);
69+
}
70+
return this.client_.unaryCall(
71+
this.hostname_ +
72+
'/jump.JumpService/Jump',
73+
request,
74+
metadata || {},
75+
this.methodInfoJump);
76+
}
77+
78+
}
79+

src/utils/grpc/jump_pb.d.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as jspb from 'google-protobuf'
2+
3+
4+
5+
export class JumpReq extends jspb.Message {
6+
getCount(): number;
7+
setCount(value: number): JumpReq;
8+
9+
getMessage(): string;
10+
setMessage(value: string): JumpReq;
11+
12+
getJumpsList(): Array<string>;
13+
setJumpsList(value: Array<string>): JumpReq;
14+
clearJumpsList(): JumpReq;
15+
addJumps(value: string, index?: number): JumpReq;
16+
17+
serializeBinary(): Uint8Array;
18+
toObject(includeInstance?: boolean): JumpReq.AsObject;
19+
static toObject(includeInstance: boolean, msg: JumpReq): JumpReq.AsObject;
20+
static serializeBinaryToWriter(message: JumpReq, writer: jspb.BinaryWriter): void;
21+
static deserializeBinary(bytes: Uint8Array): JumpReq;
22+
static deserializeBinaryFromReader(message: JumpReq, reader: jspb.BinaryReader): JumpReq;
23+
}
24+
25+
export namespace JumpReq {
26+
export type AsObject = {
27+
count: number,
28+
message: string,
29+
jumpsList: Array<string>,
30+
}
31+
}
32+
33+
export class Response extends jspb.Message {
34+
getCode(): number;
35+
setCode(value: number): Response;
36+
37+
getMessage(): string;
38+
setMessage(value: string): Response;
39+
40+
serializeBinary(): Uint8Array;
41+
toObject(includeInstance?: boolean): Response.AsObject;
42+
static toObject(includeInstance: boolean, msg: Response): Response.AsObject;
43+
static serializeBinaryToWriter(message: Response, writer: jspb.BinaryWriter): void;
44+
static deserializeBinary(bytes: Uint8Array): Response;
45+
static deserializeBinaryFromReader(message: Response, reader: jspb.BinaryReader): Response;
46+
}
47+
48+
export namespace Response {
49+
export type AsObject = {
50+
code: number,
51+
message: string,
52+
}
53+
}
54+

0 commit comments

Comments
 (0)