مقدمه
در این آموزش ، با استفاده از GraphQL API با چارچوب Vesper ، TypeORM و MySQL به عنوان یک پایگاه داده ، یک برنامه ردیابی سلامت ایجاد خواهید کرد. اینها چارچوب های Node هستند و شما از TypeScript برای زبان استفاده خواهید کرد. برای کلاینت ، برای گفتگو با API از React ، Restrstrap و Apollo Client استفاده خواهید کرد. هنگامی که این محیط کار را دارید ، تأیید اعتبار کاربر امن را با Okta اضافه خواهید کرد. Okta یک سرویس ابری است که به توسعه دهندگان اجازه می دهد تا حساب های کاربری و داده های اکانت را ذخیره و ایمن کنند و آنها را با یک یا چندین برنامه کاربردی متصل نمایند.
قبل از استفاده از این آموزش ، برای یک حساب کاربری آزاد Okta ثبت نام کنید.
مرحله 1 – ساختن API با TypeORM ، GraphQL و Vesper
TypeORM یک چارچوب ORM (object-relational mapper) است که می تواند در اکثر سیستم عامل های جاوا اسکریپت از جمله Node ، مرورگر ، Cordova ، React Native و Electron اجرا شود. و به شدت تحت تأثیر چارچوب Hibernate ، Doctrine و Entity است.
TypeORM را در سطح جهانی نصب کنید تا API خود را شروع کنید:
⦁ $ npm i -g typeorm@0.2.7

برای نگه داشتن سرویس گیرنده React و API GraphQL یک دایرکتوری ایجاد کنید:
⦁ $ mkdir health-tracker

⦁ $ cd health-tracker

با استفاده از دستور زیر یک پروژه جدید با MySQL ایجاد کنید:
⦁ $ typeorm init –name graphql-api –database mysql

برای سفارشی کردن نام کاربری ، گذرواژه و پایگاه داده ، Graphql-api / ormconfig.json ویرایش کنید.
graphql-api/ormconfig.json
{

“username”: “health”,
“password”: “pointstest”,
“database”: “healthpoints”,

}

توجه: برای دیدن درخواست هایی که مخالف MySQL اجرا میشوند، مقدار logging  را در این فایل به all تغییر دهید. بسیاری از گزینه های ورود به سیستم نیز موجود هستند.

MySQL را نصب کنید
اگر قبلاً MySQL را نصب نکرده اید ، آن را نصب کنید. در اوبونتو ، می توانید از sudo apt-get install mysql-server استفاده کنید. در macOS ، می توانید از Homebrew و brew install mysql استفاده کنید. برای ویندوز می توانید از MySQL Installer استفاده کنید.
پس از نصب MySQL با رمزعبور اصلی ، وارد سیستم شوید و یک پایگاه داده Healthpoints ایجاد کنید.
⦁ $ mysql -u root -p

Mysql> create database healthpoints;
Mysql> use healthpoints;
Mysql> grant all privileges on *.* to ‘health’@’localhost’ identified by ‘points’;

در یک پنجره پایانه به پروژه Graphql-api خود بروید ، متعلقات پروژه را نصب کنید ، سپس آن را شروع کنید تا اطمینان حاصل کنید که می توانید به MySQL وصل شوید.
⦁ $ cd graphql-api

⦁ $ npm i

⦁ $ npm start

خروجی زیر را مشاهده خواهید کرد:
Inserting a new user into the database…
Saved a new user with id: 1
Loading users from the database…
Loaded users: [ User { id: 1, firstName: ‘Timber’, lastName: ‘Saw’, age: 25 } ]
Here you can setup and run express/koa/any other framework.

نصب Vesper برای ادغام TypeORM و GraphQL
Vesper یک چارچوب Node است که TypeORM و GraphQL را ادغام می کند. برای نصب آن از npm استفاده کنید:
⦁ npm i vesper@0.1.9

اکنون زمان آن رسیده است که برخی از مدل های GraphQL (که داده های شما را مشخص می کند) و برخی از کنترل کننده ها (که نحوه تعامل با داده های شما را توضیح می دهند) را تهیه کنید.
Graphql-api / src / schema / model / Points.graphql را ایجاد کنید:
graphql-api/src/schema/model/Points.graphql
type Points {
id: Int
date: Date
exercise: Int
diet: Int
alcohol: Int
notes: String
user: User
}

Graphql-api / src / schema / model / User.graphql را ایجاد کنید:
graphql-api/src/schema/model/User.graphql
type User {
id: String
firstName: String
lastName: String
points: [Points]
}

در مرحله بعدی ، یک Graphql-api / src / schema / controller / PointsController.graphql با نمایش درخواست ها و جهش ها ایجاد کنید:
graphql-api/src/schema/controller/PointsController.graphql
type Query {
points: [Points]
pointsGet(id: Int): Points
users: [User]
}

type Mutation {
pointsSave(id: Int, date: Date, exercise: Int, diet: Int, alcohol: Int, notes: String): Points
pointsDelete(id: Int): Boolean
}

اکنون که داده های شما دارای ابرداده GraphQL است ، واحدهایی ایجاد کنید که توسط TypeORM مدیریت شود.
src / entity / User.ts را عوض کنید تا کد زیر را داشته باشد و اجازه ارتباط با کاربر توسط پوینت ها را بدهد.
src/entity/User.ts
import { Column, Entity, OneToMany, PrimaryColumn } from ‘typeorm’;
import { Points } from ‘./Points’;

@Entity()
export class User {

@PrimaryColumn()
id: string;

@Column()
firstName: string;

@Column()
lastName: string;

@OneToMany(() => Points, points => points.user)
points: Points[];
}

در همان دیرکتوری src / units ، یک کلاس Points.ts را با کد زیر ایجاد کنید.
Points.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from ‘typeorm’;
import { User } from ‘./User’;

@Entity()
export class Points {

@PrimaryGeneratedColumn()
id: number;

@Column({ type: ‘timestamp’, default: () => ‘CURRENT_TIMESTAMP’})
date: Date;

@Column()
exercise: number;

@Column()
diet: number;

@Column()
alcohol: number;

@Column()
notes: string;

@ManyToOne(() => User, user => user.points, { cascade: [“insert”] })
user: User|null;
}

به گزینه cascade: [“insert”] در حاشیه @ManyToOne توجه کنید. در صورت وجود در ماهیت خود ، این گزینه به طور خودکار کاربر را وارد می کند. src / controller / PointsController.ts را برای تبدیل داده ها از درخواست ها و جهش های GraphQL ایجاد کنید.
src/controller/PointsController.ts
import { Controller, Mutation, Query } from ‘vesper’;
import { EntityManager } from ‘typeorm’;
import { Points } from ‘../entity/Points’;

@Controller()
export class PointsController {

constructor(private entityManager: EntityManager) {
}

// serves “points: [Points]” requests
@Query()
points() {
return this.entityManager.find(Points);
}

// serves “pointsGet(id: Int): Points” requests
@Query()
pointsGet({id}) {
return this.entityManager.findOne(Points, id);
}

// serves “pointsSave(id: Int, date: Date, exercise: Int, diet: Int, alcohol: Int, notes: String): Points” requests
@Mutation()
pointsSave(args) {
const points = this.entityManager.create(Points, args);
return this.entityManager.save(Points, points);
}

// serves “pointsDelete(id: Int): Boolean” requests
@Mutation()
async pointsDelete({id}) {
await this.entityManager.remove(Points, {id: id});
return true;
}
}

src / index.ts را تغییر دهید تا از bootstrap() درVesper () برای پیکربندی همه چیز استفاده کنید.
src/index.ts
import { bootstrap } from ‘vesper’;
import { PointsController } from ‘./controller/PointsController’;
import { Points } from ‘./entity/Points’;
import { User } from ‘./entity/User’;

bootstrap({
port: 4000,
controllers: [
PointsController
],
entities: [
Points,
User
],
schemas: [
__dirname + ‘/schema/**/*.graphql’
],
cors: true
}).then(() => {
console.log(‘Your app is up and running on http://localhost:4000. ‘ +
‘You can use playground in development mode on http://localhost:4000/playground’);
}).catch(error => {
console.error(error.stack ? error.stack : error);
});

این کد به Vesper می گوید کنترلرها ، اشخاص و نمودارهای GraphQL را برای ثبت در درگاه 4000 و فعال کردن CORS (اشتراک منابع متقابل) ثبت کنید.
API خود را با استفاده از npm start شروع کرده و به http: // localhost: 4000 / playground بروید. در قسمت سمت چپ ، جهش زیر را وارد کرده و دکمه play را فشار دهید. کد زیر را تایپ کنید تا بتوانید تکمیل کدی را که GraphQL برای شما فراهم می کند تجربه کنید.
mutation {
pointsSave(exercise:1, diet:1, alcohol:1, notes:”Hello World”) {
id
date
exercise
diet
alcohol
notes
}
}

نتیجه شما شبیه به این خواهد بود

می توانید روی سربرگ SCHEMA در سمت راست کلیک کنید تا نمایش داده های موجود و جهش ها را ببینید.
برای تأیید اینکه داده ها در پایگاه داده شما هستند از پوینت های زیر استفاده کنید:
query {
points {id date exercise diet notes}
}

تنظیم تاریخ ها
ممکن است توجه کرده باشید که تاریخ برگشتی از pointsSave و درخواست پوینت ها در قالبی است که درک آن برای یک کلاینت JavaScript دشوار است. می توانید با نصب Graphql-iso-date آن را برطرف کنید.
⦁ $ npm i graphql-iso-date@3.5.0

سپس ، ورودی در src / index.ts اضافه کنید و راه حل های سفارشی را برای انواع مختلف تاریخ پیکربندی کنید. این مثال فقط از Date استفاده می کند ، اما شناختن گزینه های دیگر مفید است.
src/index.ts
import { GraphQLDate, GraphQLDateTime, GraphQLTime } from ‘graphql-iso-date’;

bootstrap({

// https://github.com/vesper-framework/vesper/issues/4
customResolvers: {
Date: GraphQLDate,
Time: GraphQLTime,
DateTime: GraphQLDateTime
},

});

اکنون اجرای درخواست points نتیجه مشتری پسند تری بر می گرداند.
{
“data”: {
“points”: [
{
“id”: 1,
“date”: “2018-06-04”,
“exercise”: 1,
“diet”: 1,
“notes”: “Hello World”
}
]
}
}

اکنون API را با GraphQL و TypeScript نوشتید. در بخش های بعدی ، یک سرویس گیرنده React برای این API ایجاد می کنید و تأیید اعتبار را با OIDC اضافه می کنید. افزودن تأیید اعتبار ، به شما این امکان را می دهد تا اطلاعات کاربر را بدست آورید و کاربر را با امتیازات آنها در ارتباط قرار دهید.
مرحله 2 – شروع کار با React
یکی از سریعترین راه های شروع کار با React استفاده از برنامه ایجاد React است.
آخرین نسخه را با استفاده از این دستور نصب کنید:
⦁ $ npm i -g create-react-app@1.1.4

به دایرکتوری که API GraphQL خود را ایجاد کرده اید بروید و یک کلاینت React ایجاد کنید:
⦁ $ cd health-tracker

⦁ $ create-react-app react-client

در مرحله بعد ، متعلقاتی را که برای ادغام Apollo Client با React و همچنین Bootstrap و reactstrap نیاز دارید، نصب کنید.
⦁ $ npm i apollo-boost@0.1.7 react-apollo@2.1.4 graphql-tag@2.9.2 graphql@0.13.2

پیکربندی کلاینت Apollo برای API شما
re-client / src / App.js را باز کنید ، ApolloClient را از apollo-boost وارد کنید و نقطه پایانی را به API GraphQL خود اضافه کنید:
react-client/src/App.js
import ApolloClient from ‘apollo-boost’;

const client = new ApolloClient({
uri: “http://localhost:4000/graphql”
});

با سه خط کد ، برنامه شما آماده است تا داده ها را دریافت کند. می توانید با وارد کردن تابع gql از Graphql-ta آن را تست کنید. با این کار رشته درخواست شما تجزیه می شود و به یک سند درخواست تبدیل می شود:
import gql from ‘graphql-tag’;

class App extends Component {

componentDidMount() {
client.query({
query: gql`
{
points {
id date exercise diet alcohol notes
}
}
`
})
.then(result => console.log(result));
}

}

حتماً ابزارهای توسعه دهنده مرورگر خود را باز کنید تا بتوانید داده ها را پس از انجام این تغییر مشاهده کنید. می توانید Console.log () را برای استفاده از this.setState({points: results.data.points}) تغییر دهید ، اما پس از آن باید حالت پیش فرض را در constructor مقداردهی کنید. اما یک روش کارآمد تر وجود دارد: می توانید از مولفه های ApolloProvider و Query از react-apollo استفاده کنید.
در زیر یک نسخه اصلاح شده از reag-client / src / App.js آمده است که از این مؤلفه ها استفاده می کند.
import React, { Component } from ‘react’;
import logo from ‘./logo.svg’;
import ‘./App.css’;
import ApolloClient from ‘apollo-boost’;
import gql from ‘graphql-tag’;
import { ApolloProvider, Query } from ‘react-apollo’;
const client = new ApolloClient({
uri: “http://localhost:4000/graphql”
});

class App extends Component {

render() {
return (
<ApolloProvider client={client}>
<div className=”App”>
<header className=”App-header”>
<img src={logo} className=”App-logo” alt=”logo” />
<h1 className=”App-title”>Welcome to React</h1>
</header>
<p className=”App-intro”>
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<Query query={gql`
{
points {id date exercise diet alcohol notes}
}
`}>
{({loading, error, data}) => {
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error}</p>;
return data.points.map(p => {
return <div key={p.id}>
<p>Date: {p.date}</p>
<p>Points: {p.exercise + p.diet + p.alcohol}</p>
<p>Notes: {p.notes}</p>
</div>
})
}}
</Query>
</div>
</ApolloProvider>
);
}
}

export default App;

اکنون یک API GraphQL و یک React UI ایجاد کرده اید که با آن صحبت می کند. با این حال ، هنوز کارهای دیگری باید انجام شود. در بخش های بعدی ، تأیید اعتبار را به React اضافه می کنید ، JWT را با Vesper تأیید می کنید و عملکرد CRUD را به UI اضافه می کنید. عملکرد CRUD به لطف جهش هایی که قبلاً نوشتید در API وجود دارد.
مرحله 3 – اضافه کردن احراز هویت برای React با OpenID Connect
برای استفاده از Okta برای تأیید اعتبار ، باید React را پیکربندی کنید. برای این کار باید یک برنامه OIDC در Okta ایجاد کنید.
به حساب Okta Developer خود وارد شوید (اگر حساب ندارید ثبت نام کنید) و به قسمت Applications > Add Application بروید. روی Single-Page App کلیک کنید ، روی Next کلیک کنید و نامی را به برنامه بدهید که بعدا به یاد خواهید آورد. همه موارد localhost: 8080 را به localhost: 3000 تغییر دهید و روی Done کلیک کنید.
تنظیمات شما شبیه به تصویر زیر خواهد بود:

React SDK Okta به شما امکان می دهد OIDC را در یک برنامه React ادغام کنید. برای نصب ، دستورات زیر را اجرا کنید:
⦁ $ npm i ⦁ @okta/okta-react@1.0.2 react-router-dom@4.2.2

React SDK Okta به react-router وابسته است ، بنابراین شما نیاز به نصب react-router-dom دارید. پیکربندی مسیریابی در client / src / App.tsx یک رویه معمول است ، بنابراین کد آن را با کد JavaScript زیر که تأیید اعتبار با Okta است جایگزین کنید.
client/src/App.tsx
import React, { Component } from ‘react’;
import { BrowserRouter as Router, Route } from ‘react-router-dom’;
import { ImplicitCallback, SecureRoute, Security } from ‘@okta/okta-react’;
import Home from ‘./Home’;
import Login from ‘./Login’;
import Points from ‘./Points’;

function onAuthRequired({history}) {
history.push(‘/login’);
}

class App extends Component {
render() {
return (
<Router>
<Security issuer=’https://{yourOktaDomain}.com/oauth2/default’
client_id='{yourClientId}’
redirect_uri={window.location.origin + ‘/implicit/callback’}
onAuthRequired={onAuthRequired}>
<Route path=’/’ exact={true} component={Home}/>
<SecureRoute path=’/points’ component={Points}/>
<Route path=’/login’ render={() => <Login baseUrl=’https://{yourOktaDomain}.com’/>}/>
<Route path=’/implicit/callback’ component={ImplicitCallback}/>
</Security>
</Router>
);
}
}

export default App;

حتما {yourOktaDomain} و {yourClientId} را در کد قبلی جایگزین کنید. دامنه Okta شما باید چیزی شبیه به dev-12345.oktapreview باشد. اطمینان حاصل کنید که دو .com در آخر URL نباشد.
کد موجود در App.js به دو مؤلفه ای که هنوز وجود ندارد اشاره دارد: Home ، Login و Points. src / Home.js را با کد زیر ایجاد کنید. این مؤلفه مسیر پیش فرض را ارائه می دهد ، دکمه ورود به سیستم را ارائه می دهد ، و پس از ورود به سیستم به نقاط شما پیوند می زند:
src/Home.js
import React, { Component } from ‘react’;
import { withAuth } from ‘@okta/okta-react’;
import { Button, Container } from ‘reactstrap’;
import AppNavbar from ‘./AppNavbar’;
import { Link } from ‘react-router-dom’;

export default withAuth(class Home extends Component {
constructor(props) {
super(props);
this.state = {authenticated: null, userinfo: null, isOpen: false};
this.checkAuthentication = this.checkAuthentication.bind(this);
this.checkAuthentication();
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
}

async checkAuthentication() {
const authenticated = await this.props.auth.isAuthenticated();
if (authenticated !== this.state.authenticated) {
if (authenticated && !this.state.userinfo) {
const userinfo = await this.props.auth.getUser();
this.setState({authenticated, userinfo});
} else {
this.setState({authenticated});
}
}
}

async componentDidMount() {
this.checkAuthentication();
}

async componentDidUpdate() {
this.checkAuthentication();
}

async login() {
this.props.auth.login(‘/’);
}

async logout() {
this.props.auth.logout(‘/’);
this.setState({authenticated: null, userinfo: null});
}

render() {
if (this.state.authenticated === null) return null;
const button = this.state.authenticated ?
<div>
<Button color=”link”><Link to=”/points”>Manage Points</Link></Button><br/>
<Button color=”link” onClick={this.logout}>Logout</Button>
</div>:
<Button color=”primary” onClick={this.login}>Login</Button>;

const message = this.state.userinfo ?
<p>Hello, {this.state.userinfo.given_name}!</p> :
<p>Please log in to manage your points.</p>;

return (
<div>
<AppNavbar/>
<Container fluid>
{message}
{button}
</Container>
</div>
);
}
});

این مؤلفه از <Container /> و <Button /> استفاده می کند. reactstrap را نصب کنید تا همه چیز کامپایل شود. بستگی به Bootstrap دارد ، بنابراین آن را نیز در بر داشته باشید.
⦁ $ npm i reactstrap@6.1.0 bootstrap@4.1.1

فایل CSS Bootstrap را به عنوان ورودی src / index.js اضافه کنید.
src/index.js
import ‘bootstrap/dist/css/bootstrap.min.css’;

شما ممکن است متوجه شوید که یک <AppNavbar /> در مولفه home روش render()وجود دارد. src / AppNavbar.js را ایجاد کنید تا بتوانید از یک هدر مشترک بین اجزا استفاده کنید.
import React, { Component } from ‘react’;
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from ‘reactstrap’;
import { Link } from ‘react-router-dom’;

export default class AppNavbar extends Component {
constructor(props) {
super(props);
this.state = {isOpen: false};
this.toggle = this.toggle.bind(this);
}

toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}

render() {
return <Navbar color=”success” dark expand=”md”>
<NavbarBrand tag={Link} to=”/”>Home</NavbarBrand>
<NavbarToggler onClick={this.toggle}/>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className=”ml-auto” navbar>
<NavItem>
<NavLink
href=”https://twitter.com/oktadev”>@oktadev</NavLink>
</NavItem>
<NavItem>
<NavLink href=”https://github.com/oktadeveloper/okta-react-graphql-example/”>GitHub</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>;
}
}

در این مثال ، ویجت ورود به سیستم Okta را جاسازی خواهید کرد. گزینه دیگر تغییر مسیر به Okta و استفاده از صفحه ورود به سیستم میزبان است.
ویجت ورود به سیستم را با استفاده از npm نصب کنید:
⦁ $ npm i ⦁ @okta/okta-signin-widget@2.9.0

src / Login.js را ایجاد کنید و کد زیر را به آن اضافه کنید.
src/Login.js
import React, { Component } from ‘react’;
import { Redirect } from ‘react-router-dom’;
import OktaSignInWidget from ‘./OktaSignInWidget’;
import { withAuth } from ‘@okta/okta-react’;

export default withAuth(class Login extends Component {
constructor(props) {
super(props);
this.onSuccess = this.onSuccess.bind(this);
this.onError = this.onError.bind(this);
this.state = {
authenticated: null
};
this.checkAuthentication();
}

async checkAuthentication() {
const authenticated = await this.props.auth.isAuthenticated();
if (authenticated !== this.state.authenticated) {
this.setState({authenticated});
}
}

componentDidUpdate() {
this.checkAuthentication();
}

onSuccess(res) {
return this.props.auth.redirect({
sessionToken: res.session.token
});
}

onError(err) {
console.log(‘error logging in’, err);
}

render() {
if (this.state.authenticated === null) return null;
return this.state.authenticated ?
<Redirect to={{pathname: ‘/’}}/> :
<OktaSignInWidget
baseUrl={this.props.baseUrl}
onSuccess={this.onSuccess}
onError={this.onError}/>;
}
});

مؤلفه Login به OktaSignInWidget اشاره دارد. src / OktaSignInWidget.js را ایجاد کنید:
src/OktaSignInWidget.js
import React, {Component} from ‘react’;
import ReactDOM from ‘react-dom’;
import OktaSignIn from ‘@okta/okta-signin-widget’;
import ‘@okta/okta-signin-widget/dist/css/okta-sign-in.min.css’;
import ‘@okta/okta-signin-widget/dist/css/okta-theme.css’;
import ‘./App.css’;

export default class OktaSignInWidget extends Component {
componentDidMount() {
const el = ReactDOM.findDOMNode(this);
this.widget = new OktaSignIn({
baseUrl: this.props.baseUrl
});
this.widget.renderEl({el}, this.props.onSuccess, this.props.onError);
}

componentWillUnmount() {
this.widget.remove();
}

render() {
return <div/>;
}
};

src / Points.js را برای ارائه لیست امتیاز از API خود ایجاد کنید:
import React, { Component } from ‘react’;
import { ApolloClient } from ‘apollo-client’;
import { createHttpLink } from ‘apollo-link-http’;
import { setContext } from ‘apollo-link-context’;
import { InMemoryCache } from ‘apollo-cache-inmemory’;
import gql from ‘graphql-tag’;
import { withAuth } from ‘@okta/okta-react’;
import AppNavbar from ‘./AppNavbar’;
import { Alert, Button, Container, Table } from ‘reactstrap’;
import PointsModal from ‘./PointsModal’;

export const httpLink = createHttpLink({
uri: ‘http://localhost:4000/graphql’
});

export default withAuth(class Points extends Component {
client;

constructor(props) {
super(props);
this.state = {points: [], error: null};

this.refresh = this.refresh.bind(this);
this.remove = this.remove.bind(this);
}

refresh(item) {
let existing = this.state.points.filter(p => p.id === item.id);
let points = […this.state.points];
if (existing.length === 0) {
points.push(item);
this.setState({points});
} else {
this.state.points.forEach((p, idx) => {
if (p.id === item.id) {
points[idx] = item;
this.setState({points});
}
})
}
}

remove(item, index) {
const deletePoints = gql`mutation pointsDelete($id: Int) { pointsDelete(id: $id) }`;

this.client.mutate({
mutation: deletePoints,
variables: {id: item.id}
}).then(result => {
if (result.data.pointsDelete) {
let updatedPoints = […this.state.points].filter(i => i.id !== item.id);
this.setState({points: updatedPoints});
}
});
}

componentDidMount() {
const authLink = setContext(async (_, {headers}) => {
const token = await this.props.auth.getAccessToken();
const user = await this.props.auth.getUser();

// return the headers to the context so httpLink can read them
return {
headers: {
…headers,
authorization: token ? `Bearer ${token}` : ”,
‘x-forwarded-user’: user ? JSON.stringify(user) : ”
}
}
});

this.client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
connectToDevTools: true
});

this.client.query({
query: gql`
{
points {
id,
user {
id,
lastName
}
date,
alcohol,
exercise,
diet,
notes
}
}`
}).then(result => {
this.setState({points: result.data.points});
}).catch(error => {
this.setState({error: <Alert color=”danger”>Failure to communicate with API.</Alert>});
});
}

render() {
const {points, error} = this.state;
const pointsList = points.map(p => {
const total = p.exercise + p.diet + p.alcohol;
return <tr key={p.id}>
<td style={{whiteSpace: ‘nowrap’}}><PointsModal item={p} callback={this.refresh}/></td>
<td className={total <= 1 ? ‘text-danger’ : ‘text-success’}>{total}</td>
<td>{p.notes}</td>
<td><Button size=”sm” color=”danger” onClick={() => this.remove(p)}>Delete</Button></td>
</tr>
});

return (
<div>
<AppNavbar/>
<Container fluid>
{error}
<h3>Your Points</h3>
<Table>
<thead>
<tr>
<th width=”10%”>Date</th>
<th width=”10%”>Points</th>
<th>Notes</th>
<th width=”10%”>Actions</th>
</tr>
</thead>
<tbody>
{pointsList}
</tbody>
</Table>
<PointsModal callback={this.refresh}/>
</Container>
</div>
);
}
})

این کد با روشهای refresh () و remove () شروع می شود. قسمت مهم در componentDidMount() اتفاق می افتد ، جایی که نشانه دسترسی در یک هدر Authorization  اضافه می شود و اطلاعات کاربر در یک هدر کاربر x ارسال می شود. ApolloClient با این اطلاعات ایجاد می شود ، یک حافظه پنهان اضافه می شود و فلگ connToDevTools روشن می شود. این کار می تواند برای اشکال زدایی با ابزارهای توسعه دهنده کلاینت Apollo مفید باشد.
componentDidMount() {
const authLink = setContext(async (_, {headers}) => {
const token = await this.props.auth.getAccessToken();

// return the headers to the context so httpLink can read them
return {
headers: {
…headers,
authorization: token ? `Bearer ${token}` : ”,
‘x-forwarded-user’: user ? JSON.stringify(user) : ”
}
}
});

this.client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
connectToDevTools: true
});

// this.client.query(…);
}

احراز هویت با Apollo Client نیاز به متعلقات جدید دارد. اکنون این موارد را نصب کنید:
⦁ npm apollo-link-context@1.0.8 apollo-link-http@1.5.4

در JSX صفحه یک دکمه حذف وجود دارد که روش remove() را در Points فراخوانی میکند. یک مؤلفه <PointsModal /> نیز وجود دارد. که برای هر مورد ارجاع دهی شده و همچنین در پایین ذکر شده است. متوجه خواهید شد که هر دو روش refresh() را ارجاع میدهند که لیست را به روز می کند.
<PointsModal item={p} callback={this.refresh}/>
<PointsModal callback={this.refresh}/>

این مولفه در زمانی که هیچ آیتمی تنظیم نشده است ، پیوندی را برای ویرایش یک جزء ارائه میدهد یا دکمه ای اضافه میکند.
src / PointsModal.js را ایجاد کنید و کد زیر را به آن اضافه کنید.
src/PointsModal.js
import React, { Component } from ‘react’;
import { Button, Form, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader } from ‘reactstrap’;
import { withAuth } from ‘@okta/okta-react’;
import { httpLink } from ‘./Points’;
import { ApolloClient } from ‘apollo-client’;
import { setContext } from ‘apollo-link-context’;
import { InMemoryCache } from ‘apollo-cache-inmemory’;
import gql from ‘graphql-tag’;
import { Link } from ‘react-router-dom’;

export default withAuth(class PointsModal extends Component {
client;
emptyItem = {
date: (new Date()).toISOString().split(‘T’)[0],
exercise: 1,
diet: 1,
alcohol: 1,
notes: ”
};

constructor(props) {
super(props);
this.state = {
modal: false,
item: this.emptyItem
};

this.toggle = this.toggle.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

componentDidMount() {
if (this.props.item) {
this.setState({item: this.props.item})
}

const authLink = setContext(async (_, {headers}) => {
const token = await this.props.auth.getAccessToken();
const user = await this.props.auth.getUser();

// return the headers to the context so httpLink can read them
return {
headers: {
…headers,
authorization: token ? `Bearer ${token}` : ”,
‘x-forwarded-user’: JSON.stringify(user)
}
}
});

this.client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
}

toggle() {
if (this.state.modal && !this.state.item.id) {
this.setState({item: this.emptyItem});
}
this.setState({modal: !this.state.modal});
}

render() {
const {item} = this.state;
const opener = item.id ? <Link onClick={this.toggle} to=”#”>{this.props.item.date}</Link> :
<Button color=”primary” onClick={this.toggle}>Add Points</Button>;

return (
<div>
{opener}
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{(item.id ? ‘Edit’ : ‘Add’)} Points</ModalHeader>
<ModalBody>
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<Label for=”date”>Date</Label>
<Input type=”date” name=”date” id=”date” value={item.date}
onChange={this.handleChange}/>
</FormGroup>
<FormGroup check>
<Label check>
<Input type=”checkbox” name=”exercise” id=”exercise” checked={item.exercise}
onChange={this.handleChange}/>{‘ ‘}
Did you exercise?
</Label>
</FormGroup>
<FormGroup check>
<Label check>
<Input type=”checkbox” name=”diet” id=”diet” checked={item.diet}
onChange={this.handleChange}/>{‘ ‘}
Did you eat well?
</Label>
</FormGroup>
<FormGroup check>
<Label check>
<Input type=”checkbox” name=”alcohol” id=”alcohol” checked={item.alcohol}
onChange={this.handleChange}/>{‘ ‘}
Did you drink responsibly?
</Label>
</FormGroup>
<FormGroup>
<Label for=”notes”>Notes</Label>
<Input type=”textarea” name=”notes” id=”notes” value={item.notes}
onChange={this.handleChange}/>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button color=”primary” onClick={this.handleSubmit}>Save</Button>{‘ ‘}
<Button color=”secondary” onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
)
};

handleChange(event) {
const target = event.target;
const value = target.type === ‘checkbox’ ? (target.checked ? 1 : 0) : target.value;
const name = target.name;
let item = {…this.state.item};
item[name] = value;
this.setState({item});
}

handleSubmit(event) {
event.preventDefault();
const {item} = this.state;

const updatePoints = gql`
mutation pointsSave($id: Int, $date: Date, $exercise: Int, $diet: Int, $alcohol: Int, $notes: String) {
pointsSave(id: $id, date: $date, exercise: $exercise, diet: $diet, alcohol: $alcohol, notes: $notes) {
id date
}
}`;

this.client.mutate({
mutation: updatePoints,
variables: {
id: item.id,
date: item.date,
exercise: item.exercise,
diet: item.diet,
alcohol: item.alcohol,
notes: item.notes
}
}).then(result => {
let newItem = {…item};
newItem.id = result.data.pointsSave.id;
this.props.callback(newItem);
this.toggle();
});
}
});

اطمینان حاصل کنید که بکاپ GraphQL شروع شده است ، سپس با npm start ، React را شروع کنید.
src/index.css
.container-fluid {
padding-top: 10px;
}

مؤلفه Home و یک دکمه برای ورود به سیستم را مشاهده خواهید کرد
روی login کلیک کنید و از شما خواسته می شود تا اعتبارنامه های Okta خود را وارد کنید.

پس از وارد کردن موارد اعتباری، وارد سیستم می شوید.

برای دیدن لیست امتیازها روی manage points کلیک کنید.

ظاهر React  شما ایمن شده است ، اما API شما هنوز به صورت گسترده باز است. بیایید این مشکل را برطرف کنیم.
اطلاعات کاربر را از JWT دریافت کنید
در یک پنجره ترمینال به پروژه Graphql-api خود بروید و Verified JWT Okta را نصب کنید:
⦁ $ npm i ⦁ @okta/jwt-verifier@0.0.12

برای نگه داشتن اطلاعات فعلی کاربر ، Graphql-api / src / CurrentUser.ts را ایجاد کنید.
graphql-api/src/CurrentUser.ts
export class CurrentUser {
constructor(public id: string, public firstName: string, public lastName: string) {}
}

OktaJwtVerifier و CurrentUser را در Graphql-api / src / index.ts وارد کنید و تأیید کننده JWT را برای استفاده از تنظیمات برنامه OIDC خود پیکربندی کنید.
graphql-api/src/index.ts
import * as OktaJwtVerifier from ‘@okta/jwt-verifier’;
import { CurrentUser } from ‘./CurrentUser’;

const oktaJwtVerifier = new OktaJwtVerifier({
clientId: ‘{yourClientId},
issuer: ‘https://{yourOktaDomain}.com/oauth2/default’
});
Copy
In the bootstrap configurati

در پیکربندی bootstrap ، setupContainer را معرفی کنید تا یک هدر authorization را درخواست کنید و کاربر فعلی را از هدر x-forwarded-user تنظیم کنید.
bootstrap({

cors: true,
setupContainer: async (container, action) => {
const request = action.request;
// require every request to have an authorization header
if (!request.headers.authorization) {
throw Error(‘Authorization header is required!’);
}
let parts = request.headers.authorization.trim().split(‘ ‘);
let accessToken = parts.pop();
await oktaJwtVerifier.verifyAccessToken(accessToken)
.then(async jwt => {
const user = JSON.parse(request.headers[‘x-forwarded-user’].toString());
const currentUser = new CurrentUser(jwt.claims.uid, user.given_name, user.family_name);
container.set(CurrentUser, currentUser);
})
.catch(error => {
throw Error(‘JWT Validation failed!’);
})
}

});

Graphql-api / src / controler / PointsController.ts را تغییر دهید تا CurrentUser را به عنوان یک وابستگی وارد کنید. در حالی که در آنجا هستید ، روش points() را تنظیم کنید تا توسط شناسه کاربر فیلتر شده و pointsSave() را برای تنظیم کاربر در حین ذخیره کردن، تغییر دهید
graphql-api/src/controller/PointsController.ts
import { Controller, Mutation, Query } from ‘vesper’;
import { EntityManager } from ‘typeorm’;
import { Points } from ‘../entity/Points’;
import { User } from ‘../entity/User’;
import { CurrentUser } from ‘../CurrentUser’;

@Controller()
export class PointsController {

constructor(private entityManager: EntityManager, private currentUser: CurrentUser) {
}

// serves “points: [Points]” requests
@Query()
points() {
return this.entityManager.getRepository(Points).createQueryBuilder(“points”)
.innerJoin(“points.user”, “user”, “user.id = :id”, { id: this.currentUser.id })
.getMany();
}

// serves “pointsGet(id: Int): Points” requests
@Query()
pointsGet({id}) {
return this.entityManager.findOne(Points, id);
}

// serves “pointsSave(id: Int, date: Date, exercise: Int, diet: Int, alcohol: Int, notes: String): Points” requests
@Mutation()
pointsSave(args) {
// add current user to points saved
if (this.currentUser) {
const user = new User();
user.id = this.currentUser.id;
user.firstName = this.currentUser.firstName;
user.lastName = this.currentUser.lastName;
args.user = user;
}

const points = this.entityManager.create(Points, args);
return this.entityManager.save(Points, points);
}

// serves “pointsDelete(id: Int): Boolean” requests
@Mutation()
async pointsDelete({id}) {
await this.entityManager.remove(Points, {id: id});
return true;
}
}

API را مجدداً راه اندازی کنید ، و اکنون کامل خواهد شد.

کد منبع
می توانید کد منبع این مقاله را در GitHub پیدا کنید.
نتیجه
در این مقاله نحوه ساختن یک برنامه React امن با GraphQL ، TypeORM و Node / Vesper به شما نشان داده شده است.

 

از این لینک ها زیر می توانید آمورش های بیشتری برای لینوکس پیدا کنید :

استفاده از nsh برای دستورات از راه دور اوبونتو 18 –  میزبانی وب سایت با Caddy اوبونتو 18

تنظیم سرور ذخیره سازی آبجکت با استفاده از Minio در اوبونتو 18  –  ضبط و اشتراک گذاری ترمینال با Terminalizer اوبونتو

تنظیم مسیریابی شرطی و پاسخگو با React Router v4  –  ایجاد یک URL کوتاه کننده با Django و GraphQL

یک برنامه ردیابی سلامت را با React ،GraphQL و Okta –  ساخت برنامه چت زمان حقیقی React و GraphQL

به روزرسانی فیلترهای مرتب سازی Angular (زاویه ای) –  با استفاده از React ، Superagent و API اینستاگرام

نحوه ساختن یک برنامه جهانی با Nuxt.js و Django –  دکمه دانلود با ریزتعاملات با CSS ، anime.js و segment.js

نحوه اضافه کردن عکسهای پیشرفته در Node و Express  –  با Vue ،GraphQL و Apollo Client یک وبلاگ ساخت

یک برنامه SSR با روتر Preact ، Unistore و Preact بسازید  –  ساخت برنامه های وب پیشرونده با Angular

اشکال زدایی JavaScript در تولید با نقشه های منبع  –  می توان با Koa برنامه “سلام جهانی” ساخت

ساختن یک برنامه با Node ، React ، Okta  –   مدیریت حالت فرم در React با Redux Form

نحوه تنظیم Laravel ، Nginx و MySQL  –  ارتقاء از AngularJS به Angular با ngUpgrade

استفاده از ویژوال استودیو از راه دور  –  احراز هویت API با JSON Web Tokens و Passport

راه اندازی یک پروژه React با Parcel  –  ایجاد Swiper مانند Netflix را در Vue

ساختن یک ربات تلگرام با Laravel و BotMan  –  استفاده از map، filter، و reduce در جاوااسکریپت

چگونه می توان موتور جستجوی زمان واقعی را با Vue  –  ساختن سیستم مستندات (Documentation) با Vue و VuePress

استفاده از اشتراک زنده با کد ویژوال استودیو  –  ساخت یک مقیاس اندازه گیری قدرت رمز عبور را در React

شروع عملی GraphQL با Node.js و Express  –  ساخت یک برنامه آب و هوا در Django

نحوه نصب Discourse روی Ubuntu 18  –  تأیید رمز عبور با استفاده از درخواست فرم Laravel

نحوه نصب MySQL در CentOS 8  –  استفاده از پسوند PDO PHP برای انجام تراکنش MySQL

نصب و پیکربندی SNMP Daemon و Client در Ubuntu 18  –  نصب Linux، Nginx، MariaDB،PHP در Debian 10

 

 

کلمات کلیدی خرید سرور

خرید vps – خرید سرور مجازی – خرید سرور – سرور هلند – فروش vps – سرور مجازی آمریکا – خریدvps – سرور مجازی هلند – فروش سرور مجازی – سرور آمریکا – vps – سرور مجازی انگلیس – سرور مجازی آلمان – سرور مجازی کانادا – خرید vps آمریکا – خرید وی پی اس – سرور – خرید سرور مجازی هلند – vps خرید – سرور مجازی فرانسه – سرور مجازی هلند – خرید vps آمریکاخرید سرور مجازی ارزان هلندvpsخرید vps هلندخرید سرور مجازی آمریکاخرید vps فرانسهتست vpsسرور مجازی تستسرور مجازی ویندوزارزانترین vpsخرید وی پی اسvps ارزان – 

برچسب‌ها:, , , , ,