Skip to content
Snippets Groups Projects
Commit 906aa38a authored by zshan2's avatar zshan2
Browse files

Merge branch 'assignment-2.2' into 'master'

Implementation of assignment-2.2

See merge request !2
parents a8f8fb15 d8e2853e
Branches master
No related tags found
1 merge request!2Implementation of assignment-2.2
Showing
with 638 additions and 70 deletions
ManualTestPictures/1(41).png

147 KiB

ManualTestPictures/1(42).png

150 KiB

ManualTestPictures/1(43).png

170 KiB

ManualTestPictures/1(5).png

109 KiB

ManualTestPictures/1(6).png

109 KiB

ManualTestPictures/1(7).png

110 KiB

ManualTestPictures/1(8).png

102 KiB

ManualTestPictures/1(9).png

154 KiB

......@@ -3,12 +3,13 @@ This project is for sp21 CS242 assignment2
Current version is 2.1
###Function
### Function
1. Scrap data of books and authors from GoodReads
2. Store scraped data on cloud or local files
3. Query cloud for certain documents of books or authors
###Composition:
####Client: Command line interface (interactive)
####Server: Simple server based on Flask
####Database: MongoDB
\ No newline at end of file
### Composition:
#### Client: Command line interface (interactive)
#### Server: Simple server based on Flask
#### Database: MongoDB
#### Website: Javascipt HTML with React
......@@ -20,7 +20,7 @@ def parse_query_to_url(query):
if count == 2:
# can only be A.B:C or wrong
if re.search("^[0-9a-zA-Z_.]+:[0-9a-zA-Z_\".]", query):
return url_safe(elements[0] + "%3A" + elements[1])
return url_safe(elements[0] + ":3A" + elements[1])
else:
print("Invalid query1.")
return ""
......@@ -48,6 +48,12 @@ def parse_query_to_url(query):
return ""
def parse_url_to_query(url):
query_str = url.replace("%20", " ").replace("%22", "\"").replace("%3C", "<").replace("%3E", ">").replace("%26", "&")
return query_str.replace(" AND ", "&AND&").replace(" OR ", "&OR&").replace(": ", ":").replace(" > ", ">")\
.replace(" < ", "<")
def parse_query_to_json(pair):
elements = re.findall("[0-9A-Za-z\"_.]+", pair)
count = len(elements)
......@@ -61,7 +67,7 @@ def parse_query_to_json(pair):
else:
return {elements[0].split(".")[1]: {"$not": {"$regex": elements[2], "$options": "i"}}}
else:
# can be A.B: C or A.B: "C"
# can be A.B: C, A.B: "C", A.B > C, or A.B < C
if re.search(":", pair):
if re.search("^[0-9.]*$", elements[1]) and not re.search("id", elements[0]):
return {elements[0].split(".")[1]: float(elements[1])}
......@@ -71,6 +77,8 @@ def parse_query_to_json(pair):
else:
return {elements[0].split(".")[1]: {"$regex": elements[1], "$options": "i"}}
else:
if len(elements[0].split(".")) != 2:
return {"wrong": "True"}
if re.search(">", pair):
return {elements[0].split(".")[1]: {"$gt": float(elements[1])}}
elif re.search("<", pair):
......
......@@ -2,99 +2,144 @@ import DataBase.mongoDB as DataBase
import RegularExpressionParser.Parser as Parser
import re
import Crawler.Scrape as Scrape
import json
from flask import Flask
from flask import request
from flask import abort
from flask import jsonify
from urllib.parse import urlparse, parse_qs
from flask_cors import CORS, cross_origin
app = Flask(__name__)
# app.config["DEBUG"] = True
cors = CORS(app)
# cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
@app.route("/", methods=['GET'])
def home():
""" homepage of server """
return "200: successfully connected to home page\n"
@app.route("/api/book/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
def data_base_book():
print(request.url)
""" data base page of server """
if request.method == "GET":
url_parsed = urlparse(request.url)
qs_parsed = parse_qs(url_parsed.query)
if qs_parsed == {}:
return DataBase.get_documents_json(0, {})
result = search_document(["book.id:" + qs_parsed["id"][0]])
if result == {"wrong": "True"}:
abort(400, "Bad Request")
elif result == json.dumps({'books': []}):
abort(400, "Book not found")
else:
return result
elif request.method == "PUT":
if request.headers["Content-Type"] != "application/json":
abort(415, "content should be JSON file")
print(request)
json_update_info = request.json
opt = 0
DataBase.update_dicts(opt, request.args.to_dict(), json_update_info)
return "200: PUT succeeded"
elif request.method == "POST":
if request.headers["Content-Type"] != "application/json":
abort(415, "content should be JSON file")
print(request)
json_file = request.json
DataBase.insert_document(json_file, 0)
return "200: POST succeeded"
elif request.method == "DELETE":
identifier = request.args.to_dict()
print(identifier)
opt=0
deleted_count = DataBase.clean(opt, identifier)
if deleted_count > 0:
return "200: DELETE succeeded"
else:
abort(400, "Failed to delete: target not found")
elif request.method == "OPTIONS":
return '200 OK'
@app.route("/api/<collection>/", methods=["GET", "PUT", "POST", "DELETE"])
def data_base(collection):
""" data base page of server """
print("\n===============================\n")
print(collection)
print("\n===============================\n")
@app.route("/api/author/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
def data_base_author():
print(request.url)
""" data base page of server """
if request.method == "GET":
if collection == "book":
url_parsed = urlparse(request.url)
qs_parsed = parse_qs(url_parsed.query)
if qs_parsed == {}:
return jsonify(DataBase.get_documents_json(0, {}))
return jsonify(search_document(["book.id:" + qs_parsed["id"][0]]))
elif collection == "author":
url_parsed = urlparse(request.url)
qs_parsed = parse_qs(url_parsed.query)
if qs_parsed == {}:
return jsonify(DataBase.get_documents_json(1, {}))
return jsonify(search_document(["author.id:" + qs_parsed["id"][0]]))
elif collection == "search":
url_parsed = urlparse(request.url)
qs_parsed = parse_qs(url_parsed.query)
result = jsonify(search_document(qs_parsed["q"][0].split("&")))
return jsonify(result)
url_parsed = urlparse(request.url)
qs_parsed = parse_qs(url_parsed.query)
if qs_parsed == {}:
return DataBase.get_documents_json(1, {})
result = search_document(["author.id:" + qs_parsed["id"][0]])
if result == {"wrong": "True"}:
abort(400, "Bad Request")
elif result == json.dumps({'authors': []}):
abort(400, "Author not found")
else:
abort(404)
return result
elif request.method == "PUT":
if request.headers["Content-Type"] != "application/json":
abort(415)
abort(415, "content should be JSON file")
json_update_info = request.json
if collection == "book":
opt = 0
elif collection == "author":
opt = 1
else:
abort(404)
opt = 1
DataBase.update_dicts(opt, request.args.to_dict(), json_update_info)
return "200: PUT succeeded"
elif request.method == "POST":
if request.headers["Content-Type"] != "application/json":
abort(415, "content should be JSON file")
json_file = request.json
if collection == "books":
DataBase.insert_dicts(json_file, 0)
elif collection == "authors":
DataBase.insert_dicts(json_file, 1)
elif collection == "book":
DataBase.insert_document(json_file, 0)
elif collection == "author":
DataBase.insert_document(json_file, 1)
elif collection == "scrape":
param = request.args.to_dict()
url = param["url"]
max_book = param["max_book"]
max_author = param["max_author"]
Scrape.scrape_api(url, max_book, max_author)
return "200: new data has been added to database"
else:
abort(404)
DataBase.insert_document(json_file, 1)
return "200: POST succeeded"
elif request.method == "DELETE":
identifier = request.args.to_dict()
print(identifier)
if collection == "book":
opt = 0
elif collection == "author":
opt = 1
opt=1
deleted_count = DataBase.clean(opt, identifier)
if deleted_count > 0:
return "200: DELETE succeeded"
else:
abort(404, "Unknown Collection to DELETE")
DataBase.clean(opt, identifier)
return "200: DELETE succeeded"
abort(400, "Failed to delete: target not found")
elif request.method == "OPTIONS":
return '200 OK'
@app.route("/api/scrape/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
def data_base_scrape():
print(request.url)
param = request.args.to_dict()
url = param["url"]
max_book = param["max_book"]
max_author = param["max_author"]
Scrape.scrape_api(url, max_book, max_author)
return "200: new data has been added to database"
@app.route("/api/search/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
def data_base_search():
print(request.url)
url_parsed = urlparse(request.url)
query = Parser.parse_url_to_query(url_parsed.query)
qs_parsed = query.replace("q=", "")
result = search_document(qs_parsed.split("&"))
if result == {"wrong": "True"}:
abort(400, "Bad Request")
elif result == json.dumps({'books': []}):
abort(400, "Target not found")
else:
return result
# @app.route("/api/", methods=["GET", "PUT", "POST", "DELETE", "OPTIONS"])
# def data_api():
# print('====================')
# print(request.url)
# print(request.collection)
# print('====================')
# return '200: nice!'
def search_document(identifiers):
""" function used to find one or several document in database """
if len(identifiers) == 1:
json_idt = Parser.parse_query_to_json(identifiers[0])
if json_idt == {"wrong": "True"}:
return {"wrong": "True"}
print(json_idt)
if re.search("^book.*", identifiers[0]):
return DataBase.get_documents_json(0, json_idt)
......@@ -115,6 +160,8 @@ def search_document(identifiers):
opt = 1
json_idt1 = Parser.parse_query_to_json(identifiers[0])
json_idt2 = Parser.parse_query_to_json(identifiers[2])
if json_idt1 == {"wrong": "True"} or json_idt2 == {"wrong": "True"}:
return {"wrong": "True"}
if identifiers[1] == "AND":
exp = {"$and": [json_idt1, json_idt2]}
elif identifiers[1] == "OR":
......@@ -126,7 +173,10 @@ def search_document(identifiers):
print(exp)
return DataBase.get_documents_json(opt, exp)
else:
return "Error, unknown identifiers"
print("Error, unknown identifiers")
return {}
if __name__ == "__main__":
......
......@@ -34,3 +34,7 @@ class DataBaseTests(unittest.TestCase):
output_json = Parser.parse_query_to_json(query)
self.assertEqual(output_json, expect_json)
if __name__ == '__main__':
unittest.main()
......@@ -44,7 +44,7 @@ class DataBaseTests(unittest.TestCase):
db.insert_document(self.test_data1, 0)
url = "http://127.0.0.1:5000/api/bookssss?id=38746485"
res = requests.get(url)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.status_code, 400)
def test_valid_put(self):
db.insert_document(self.test_data1, 0)
......
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
import React from 'react';
import FourButtons from './Components/FourButtons';
import { Dialog } from 'react-overlay-pack';
import CorrectSign from './material/sign_correct.png';
import ErrorSign from './material/sign_error.png';
import ElementDisplay from './Components/ElementDisplay';
function App() {
/* Activate Hooks */
const { useState } = React;
/* Initializing Hooks for component values showed in main page */
const [queryStr, setQueryStr] = useState('')
const [sign, setSign] = useState(CorrectSign)
const [text, setText] = useState('This area shows the result of requests..')
const [formState, setFormState] = useState('hide')
const [dialogState, setDialogState] = useState(false)
const [renderState, setRenderState] = useState(false)
/* initializing form-like input of bookData for PUT and POST */
const [bookURLState, setBookURLState] = useState('')
const [bookTitleState, setBookTitleState] = useState('')
const [bookIDState, setBookIDState] = useState('')
const [bookISBNState, setBookISBNState] = useState('')
const [bookAuthorState, setBookAuthorState] = useState('')
const [bookAuthorURLState, setBookAuthorURLState] = useState('')
const [bookRatingState, setBookRatingState] = useState('')
const [bookRatingCountState, setBookRatingCountState] = useState('')
const [bookReviewCountState, setBookReviewCountState] = useState('')
const [bookImageURLState, setBookImageURLState] = useState('')
const [bookSimilarBooksState, setBookSimilarBooksState] = useState('')
/* Initializing form-like input of authorData for PUT AND POST */
const [authorNameState, setAuthorNameState] = useState('')
const [authorURLState, setAuthorURLState] = useState('')
const [authorIDState, setAuthorIDState] = useState('')
const [authorRatingState, setAuthorRatingState] = useState('')
const [authorRatingCountState, setAuthorRatingCountState] = useState('')
const [authorReviewCountState, setAuthorReviewCountState] = useState('')
const [authorImageURLState, setAuthorImageURLState] = useState('')
const [authorRelatedAuthorsState, setAuthorRelatedAuthorsState] = useState('')
const [authorAuthorBooksState, setAuthorAuthorBooksState] = useState('')
/* Hooks used to show whether the data passed in is bookData or authorData */
const [dataState, setDataState] = useState({})
/* Activating axios */
const axios = require('axios').default;
var center = {display: 'flex', justifyContent: 'center', alignItems: 'center'}
/** Function used in GET Requests */
function get() {
axios.get('http://127.0.0.1:5000/api/' + queryStr)
.then(function (response) {
// handle success
setSign(CorrectSign)
showDialog()
document.getElementById('dialog').value = response.status + ":\n" + response.statusText
if (queryStr.includes('book')) {
setDataState(response.data['books'][0])
} else {
setDataState(response.data['authors'][0])
}
setRenderState(true)
})
.catch(function (error) {
// handle error
setSign(ErrorSign)
showDialog()
document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
})
}
/** Function used in PUT and POST: extract user input of bookData from the form and parse into JSON */
function createJson() {
var bookData = {}
bookData.type = 'book'
/* Set corresponding value if the input is not empty */
if(bookURLState !== '') {bookData.book_url = bookURLState}
if(bookTitleState !== '') {bookData.title = bookTitleState}
if(bookIDState !== '') {bookData.id = bookIDState}
if(bookISBNState !== '') {bookData.ISBN = bookISBNState}
if(bookAuthorURLState !== '') {bookData.author_url = bookAuthorURLState}
if(bookAuthorState !== '') {bookData.author = bookAuthorState}
if(bookRatingState !== '') {bookData.rating = parseFloat(bookRatingState)}
if(bookRatingCountState !== '') {bookData.rating_count = parseInt(bookRatingCountState)}
if(bookReviewCountState !== '') {bookData.review_count = parseInt(bookReviewCountState)}
if(bookSimilarBooksState !== '') {bookData.similar_books = bookSimilarBooksState.split(', ')}
setDataState(bookData)
return bookData
}
/** Function used in PUT and POST: extract user input of authorData from the form and parse into JSON */
function createJsonAuthor() {
var authorData = {}
authorData.type = 'author'
/* Set corresponding value if the input is not empty */
if(authorNameState !== '') {authorData.name = authorNameState}
if(authorURLState !== '') {authorData.author_url = authorURLState}
if(authorIDState !== '') {authorData.id = authorIDState}
if(authorRatingState !== '') {authorData.rating = parseFloat(authorRatingState)}
if(authorRatingCountState !== '') {authorData.rating_count = parseInt(authorRatingCountState)}
if(authorReviewCountState !== '') {authorData.review_count = parseInt(authorReviewCountState)}
if(authorImageURLState !== '') {authorData.image_url = authorImageURLState}
if(authorRelatedAuthorsState !== '') {authorData.related_authors = authorRelatedAuthorsState.split(', ')}
if(authorAuthorBooksState !== '') {authorData.author_books = authorAuthorBooksState.split(',')}
setDataState(authorData)
return authorData
}
/** Function used to hide the input form */
function hideForm() {
setFormState('hide')
}
/** Function used to show bookData input form */
function changeFormStateBook() {
setRenderState(false)
if (formState === 'hide') {
setFormState('showBook')
} else {
setFormState('hide')
}
}
/** Function used to show authorData input form */
function changeFormStateAuthor() {
setRenderState(false)
if (formState === 'hide') {
setFormState('showAuthor')
} else {
setFormState('hide')
}
}
/** Function used to set Dialog visible, which is called when a response is received */
function showDialog() {
setDialogState(true)
}
/** Function used in PUT requests */
function put() {
setRenderState(false)
hideForm()
var data;
if (queryStr.includes('book')) {
data = createJson()
} else {
data = createJsonAuthor()
}
let config = {
headers: {
'Content-Type': 'application/json',
}
}
axios.put('http://127.0.0.1:5000/api/' + queryStr, data, config)
.then(function (response) {
// handle success
setSign(CorrectSign)
showDialog()
document.getElementById('dialog').value = response.status + ":\n" + response.statusText
})
.catch(function (error) {
// handle error
setSign(ErrorSign)
showDialog()
document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
})
}
/** Function used in POST requests */
function post() {
setRenderState(false)
let config = {
headers: {
'Content-Type': 'application/json',
}
}
hideForm()
var data;
if (queryStr.includes('book')) {
data = createJson()
} else {
data = createJsonAuthor()
}
axios.post('http://127.0.0.1:5000/api/' + queryStr, JSON.stringify(data), config)
.then(function (response) {
// handle success
setSign(CorrectSign)
showDialog()
document.getElementById('dialog').value = response.status + ":\n" + response.statusText
})
.catch(function (error) {
// handle error
setSign(ErrorSign);
showDialog();
document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
})
}
/** Function used in DELETE requests */
function delete_data() {
setRenderState(false)
axios.delete('http://127.0.0.1:5000/api/' + queryStr)
.then(function (response) {
// handle success
setSign(CorrectSign)
showDialog()
document.getElementById('dialog').value = response.status + ":\n" + response.statusText
})
.catch(function (error) {
// handle error
setSign(ErrorSign)
showDialog()
document.getElementById('dialog').value = error //.response.status + ":\n" + error.response.statusText
})
}
/** HTML code */
return (
<div>
{/* Dialog used when received response from server */}
{dialogState === true &&
<Dialog //Warning, not responsive!
show={dialogState}
onOutsideClick={() => setDialogState(false)}>
<div style={{marginTop:'20%'}}>
<div style={{
marginLeft: '35%'
}}>
<img alt='responseStatus' src={sign}/>
</div>
<div>
<textarea id='dialog'
readOnly={true}
rows="3"
cols="38"
style={{
border:'2px solid silver',
backgroundColor: 'white',
fontSize: '200%',
margin: '50px'
}}>
{text}
</textarea>
</div>
</div>
</Dialog>}
<h1 style={center}>
Welcome to the home page of GoodReads Crawler! </h1>
<h3 style={center}> Please input your query string:</h3>
<div style={center}>
<input
id='queryString'
type='text'
placeholder='example: book/?id=12345678'
size='40'
value={queryStr}
onChange={(e) => setQueryStr(e.target.value)}
/>
</div>
<h3 style={center}> Please input your data for uploading (only effective for POST and PUT):</h3>
{/* Buttons used to select book or author data to be passed to PUT/POST */}
{formState === 'hide' &&
<div style={center}>
<button style={{width:60}} onClick={changeFormStateBook}>
book
</button>
<button style={{marginLeft:10, width:60}} onClick={changeFormStateAuthor}>
author
</button>
</div>}
{/* Form-like input for bookData */}
{formState === 'showBook' &&
<form style={{border:'2px solid silver', marginLeft:'180px', marginRight: '180px'}}>
<div style={center}>
<input id='bookURL' style={center} type='text' size='40' value={bookURLState} placeholder='bookURL' onChange={(e) => setBookURLState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookTtile' style={center} type='text' size='40' value={bookTitleState} placeholder='bookTtile' onChange={(e) => setBookTitleState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookID' type='text' size='40' value={bookIDState} placeholder='bookID' onChange={(e) => setBookIDState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookISBN' type='text' size='40' value={bookISBNState} placeholder='bookISBN' onChange={(e) => setBookISBNState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookAuthorURL' type='text' size='40' value={bookAuthorURLState} placeholder='bookAuthorURL' onChange={(e) => setBookAuthorURLState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookAuthor' type='text' size='40' value={bookAuthorState} placeholder='bookAuthor' onChange={(e) => setBookAuthorState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookRating' type='text' size='40' value={bookRatingState} placeholder='bookRating' onChange={(e) => setBookRatingState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookRatingCount' type='text' size='40' value={bookRatingCountState} placeholder='bookRatingCount' onChange={(e) => setBookRatingCountState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookReviewCount' type='text' size='40' value={bookReviewCountState} placeholder='bookReviewCount' onChange={(e) => setBookReviewCountState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookImageURL' type='text' size='40' value={bookImageURLState} placeholder='bookImageURL' onChange={(e) => setBookImageURLState(e.target.value)}/>
</div>
<div style={center}>
<input id='bookSimilarBooks' type='text' size='40' value={bookSimilarBooksState} placeholder='bookSimilarBooks' onChange={(e) => setBookSimilarBooksState(e.target.value)}/>
</div>
</form>
}
{/* Form-like input for authorData */}
{formState === 'showAuthor' &&
<form style={{border:'2px solid silver', marginLeft:'180px', marginRight: '180px'}}>
<div style={center}>
<input id='authorName' style={center} type='text' size='40' value={authorNameState} placeholder='authorName' onChange={(e) => setAuthorNameState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorURL' style={center} type='text' size='40' value={authorURLState} placeholder='authorURL' onChange={(e) => setAuthorURLState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorID' type='text' size='40' value={authorIDState} placeholder='authorID' onChange={(e) => setAuthorIDState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorRating' type='text' size='40' value={authorRatingState} placeholder='authorRating' onChange={(e) => setAuthorRatingState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorRatingCount' type='text' size='40' value={authorRatingCountState} placeholder='authorRatingCount' onChange={(e) => setAuthorRatingCountState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorReviewCount' type='text' size='40' value={authorReviewCountState} placeholder='authorReviewCount' onChange={(e) => setAuthorReviewCountState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorImageURL' type='text' size='40' value={authorImageURLState} placeholder='authorImageURL' onChange={(e) => setAuthorImageURLState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorRelatedAuthors' type='text' size='40' value={authorRelatedAuthorsState} placeholder='authorRelatedAuthors' onChange={(e) => setAuthorRelatedAuthorsState(e.target.value)}/>
</div>
<div style={center}>
<input id='authorBooks' type='text' size='40' value={authorAuthorBooksState} placeholder='authorBooks' onChange={(e) => setAuthorAuthorBooksState(e.target.value)}/>
</div>
</form>
}
{formState !== 'hide' &&
<button style={{marginLeft:600, width:60}} onClick={hideForm}> back </button>
}
<div style={{marginTop:20, display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
{/* Fout buttons for GET PUT POST and DELETE */}
<FourButtons
backColor='blue'
borderColor='RoyalBlue'
leftMargin={0}
text='GET'
func={get} />
<FourButtons
backColor='black'
borderColor='gray'
leftMargin={20}
text='PUT'
func={put} />
<FourButtons
backColor='green'
borderColor='seagreen'
leftMargin={20}
text='POST'
func={post} />
<FourButtons
backColor='red'
borderColor='indianred'
leftMargin={20}
text='DELETE'
func={delete_data} />
</div>
{renderState === true &&
<div>
{/* Data Table used to render response json data */}
<ElementDisplay data={dataState}/>
</div>}
</div>
);
}
export default App;
\ No newline at end of file
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
import MUIDataTable from "mui-datatables";
/** MUI dataTable component, used for rendering received response */
const ElementDisplay = (props) => {
var fields = Object.keys(props.data)
var values = Object.values(props.data)
console.log(Object.values(props.data))
console.log(Object.keys(props.data))
const columns = fields
const data = [values]
const options = {
'responsive':'vertical'
}
return (
<MUIDataTable
title={"GET Data"}
data={data}
columns={columns}
options={options}
/>
)
}
export default ElementDisplay
/** Button Component for GET PUT POST DELETE */
const FourButtons = ({backColor, borderColor, leftMargin, text, func}) => {
return (
<button
style={{
width:70,
height:30,
backgroundColor:backColor,
color:'white',
marginLeft:leftMargin,
borderLeftWidth:4,
borderTopWidth:3,
borderColor:borderColor,
borderBottomWidth:3}}
className='btn'
onClick={func}> {text}
</button>
)
}
export default FourButtons
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment