mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 19:39:19 +00:00
Work towards authentication
This commit is contained in:
1
api/.babelrc
Normal file
1
api/.babelrc
Normal file
@@ -0,0 +1 @@
|
||||
{ "presets": ["env"] }
|
||||
7
api/.env
Normal file
7
api/.env
Normal file
@@ -0,0 +1,7 @@
|
||||
NEO4J_URI=bolt://localhost:7687
|
||||
NEO4J_USER=neo4j
|
||||
NEO4J_PASSWORD=letmein
|
||||
GRAPHQL_LISTEN_PORT=4001
|
||||
GRAPHQL_URI=http://localhost:4001/graphql
|
||||
|
||||
JWT_SECRET=topSecretEpicJWTKEYThing
|
||||
12
api/Dockerfile
Normal file
12
api/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM node:10
|
||||
|
||||
RUN mkdir -p /app
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json .
|
||||
RUN npm install
|
||||
COPY . .
|
||||
|
||||
EXPOSE 4000
|
||||
|
||||
CMD ["npm", "start"]
|
||||
54
api/README.md
Normal file
54
api/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# GRANDstack Starter - GraphQL API
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
Start the GraphQL service:
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
This will start the GraphQL service (by default on localhost:4000) where you can issue GraphQL requests or access GraphQL Playground in the browser:
|
||||
|
||||

|
||||
|
||||
## Configure
|
||||
|
||||
Set your Neo4j connection string and credentials in `.env`. For example:
|
||||
|
||||
*.env*
|
||||
|
||||
```
|
||||
NEO4J_URI=bolt://localhost:7687
|
||||
NEO4J_USER=neo4j
|
||||
NEO4J_PASSWORD=letmein
|
||||
```
|
||||
|
||||
Note that grand-stack-starter does not currently bundle a distribution of Neo4j. You can download [Neo4j Desktop](https://neo4j.com/download/) and run locally for development, spin up a [hosted Neo4j Sandbox instance](https://neo4j.com/download/), run Neo4j in one of the [many cloud options](https://neo4j.com/developer/guide-cloud-deployment/), or [spin up Neo4j in a Docker container](https://neo4j.com/developer/docker/). Just be sure to update the Neo4j connection string and credentials accordingly in `.env`.
|
||||
|
||||
## Deployment
|
||||
|
||||
You can deploy to any service that hosts Node.js apps, but [Zeit Now](https://zeit.co/now) is a great easy to use service for hosting your app that has an easy to use free plan for small projects.
|
||||
|
||||
To deploy your GraphQL service on Zeit Now, first install [Now Desktop](https://zeit.co/download) - you'll need to provide an email address. Then run
|
||||
|
||||
```
|
||||
now
|
||||
```
|
||||
|
||||
to deploy your GraphQL service on Zeit Now. Once deployed you'll be given a fresh URL that represents the current state of your application where you can access your GraphQL endpoint and GraphQL Playgound. For example: https://grand-stack-starter-api-pqdeodpvok.now.sh/
|
||||
|
||||
## Seeding The Database
|
||||
|
||||
Optionally you can seed the GraphQL service by executing mutations that will write sample data to the database:
|
||||
|
||||
```
|
||||
npm run seedDb
|
||||
```
|
||||
BIN
api/img/graphql-playground.png
Normal file
BIN
api/img/graphql-playground.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
7866
api/package-lock.json
generated
Normal file
7866
api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
48
api/package.json
Normal file
48
api/package.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "grand-stack-starter-api",
|
||||
"version": "0.0.1",
|
||||
"description": "API app for GRANDstack",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start-dev": "./node_modules/.bin/nodemon --exec babel-node src/index.js",
|
||||
"build": "babel src -d build; cp .env build; cp src/schema.graphql build",
|
||||
"now-build": "babel src -d build; cp .env build; cp src/schema.graphql build",
|
||||
"start": "npm run build && node build/index.js",
|
||||
"seedDb": "./node_modules/.bin/babel-node src/seed/seed-db.js"
|
||||
},
|
||||
"author": "William Lyon",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"apollo-boost": "^0.3.1",
|
||||
"apollo-cache-inmemory": "^1.5.1",
|
||||
"apollo-client": "^2.5.1",
|
||||
"apollo-link-http": "^1.5.14",
|
||||
"apollo-server": "^2.6.2",
|
||||
"body-parser": "^1.19.0",
|
||||
"dotenv": "^7.0.0",
|
||||
"graphql-tag": "^2.10.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"neo4j-driver": "^1.7.3",
|
||||
"neo4j-graphql-js": "^2.6.3",
|
||||
"node-fetch": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"husky": "^1.3.1",
|
||||
"lint-staged": "^8.1.5",
|
||||
"nodemon": "^1.18.11"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,json,css,md,graphql": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
||||
14
api/src/graphql-schema.js
Normal file
14
api/src/graphql-schema.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { neo4jgraphql } from "neo4j-graphql-js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
/*
|
||||
* Check for GRAPHQL_SCHEMA environment variable to specify schema file
|
||||
* fallback to schema.graphql if GRAPHQL_SCHEMA environment variable is not set
|
||||
*/
|
||||
|
||||
export const typeDefs = fs
|
||||
.readFileSync(
|
||||
process.env.GRAPHQL_SCHEMA || path.join(__dirname, "schema.graphql")
|
||||
)
|
||||
.toString("utf-8");
|
||||
80
api/src/index.js
Normal file
80
api/src/index.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { typeDefs } from "./graphql-schema";
|
||||
import { ApolloServer } from "apollo-server-express";
|
||||
import express from "express";
|
||||
import bodyParser from "body-parser"
|
||||
import { v1 as neo4j } from "neo4j-driver";
|
||||
import { makeAugmentedSchema } from "neo4j-graphql-js";
|
||||
import dotenv from "dotenv";
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
// set environment variables from ../.env
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
app.use(bodyParser.json())
|
||||
|
||||
/*
|
||||
* Create an executable GraphQL schema object from GraphQL type definitions
|
||||
* including autogenerated queries and mutations.
|
||||
* Optionally a config object can be included to specify which types to include
|
||||
* in generated queries and/or mutations. Read more in the docs:
|
||||
* https://grandstack.io/docs/neo4j-graphql-js-api.html#makeaugmentedschemaoptions-graphqlschema
|
||||
*/
|
||||
|
||||
const schema = makeAugmentedSchema({
|
||||
typeDefs,
|
||||
config: {
|
||||
auth: {
|
||||
isAuthenticated: true,
|
||||
hasRole: true
|
||||
}
|
||||
},
|
||||
resolvers: {
|
||||
Mutation: {
|
||||
authorizeUser(root, args, context, info) {
|
||||
const token = jwt.sign({name: args.name}, process.env.JWT_SECRET)
|
||||
return token
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Create a Neo4j driver instance to connect to the database
|
||||
* using credentials specified as environment variables
|
||||
* with fallback to defaults
|
||||
*/
|
||||
const driver = neo4j.driver(
|
||||
process.env.NEO4J_URI || "bolt://localhost:7687",
|
||||
neo4j.auth.basic(
|
||||
process.env.NEO4J_USER || "neo4j",
|
||||
process.env.NEO4J_PASSWORD || "letmein"
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* Create a new ApolloServer instance, serving the GraphQL schema
|
||||
* created using makeAugmentedSchema above and injecting the Neo4j driver
|
||||
* instance into the context object so it is available in the
|
||||
* generated resolvers to connect to the database.
|
||||
*/
|
||||
const server = new ApolloServer({
|
||||
context: ({ req }) => Object.assign(req, {driver}),
|
||||
schema: schema,
|
||||
introspection: true,
|
||||
playground: true
|
||||
});
|
||||
|
||||
// Specify port and path for GraphQL endpoint
|
||||
const port = process.env.GRAPHQL_LISTEN_PORT || 4001;
|
||||
const path = "/graphql";
|
||||
|
||||
/*
|
||||
* Optionally, apply Express middleware for authentication, etc
|
||||
* This also also allows us to specify a path for the GraphQL endpoint
|
||||
*/
|
||||
server.applyMiddleware({app, path});
|
||||
|
||||
app.listen({port, path}, () => {
|
||||
console.log(`GraphQL server ready at http://localhost:${port}${path}`);
|
||||
});
|
||||
59
api/src/schema.graphql
Normal file
59
api/src/schema.graphql
Normal file
@@ -0,0 +1,59 @@
|
||||
enum Role {
|
||||
Admin
|
||||
User
|
||||
}
|
||||
|
||||
type Todo @isAuthenticated {
|
||||
id: ID!
|
||||
title: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
authorizeUser(name: String!): String
|
||||
}
|
||||
|
||||
# type User {
|
||||
# id: ID!
|
||||
# name: String
|
||||
# friends: [User] @relation(name: "FRIENDS", direction: "BOTH")
|
||||
# reviews: [Review] @relation(name: "WROTE", direction: "OUT")
|
||||
# avgStars: Float
|
||||
# @cypher(
|
||||
# statement: "MATCH (this)-[:WROTE]->(r:Review) RETURN toFloat(avg(r.stars))"
|
||||
# )
|
||||
# numReviews: Int
|
||||
# @cypher(statement: "MATCH (this)-[:WROTE]->(r:Review) RETURN COUNT(r)")
|
||||
# recommendations(first: Int = 3): [Business] @cypher(statement: "MATCH (this)-[:WROTE]->(r:Review)-[:REVIEWS]->(:Business)<-[:REVIEWS]-(:Review)<-[:WROTE]-(:User)-[:WROTE]->(:Review)-[:REVIEWS]->(rec:Business) WHERE NOT EXISTS( (this)-[:WROTE]->(:Review)-[:REVIEWS]->(rec) )WITH rec, COUNT(*) AS num ORDER BY num DESC LIMIT $first RETURN rec")
|
||||
# }
|
||||
|
||||
# type Business {
|
||||
# id: ID!
|
||||
# name: String
|
||||
# address: String
|
||||
# city: String
|
||||
# state: String
|
||||
# avgStars: Float @cypher(statement: "MATCH (this)<-[:REVIEWS]-(r:Review) RETURN coalesce(avg(r.stars),0.0)")
|
||||
# reviews: [Review] @relation(name: "REVIEWS", direction: "IN")
|
||||
# categories: [Category] @relation(name: "IN_CATEGORY", direction: "OUT")
|
||||
# }
|
||||
|
||||
# type Review {
|
||||
# id: ID!
|
||||
# stars: Int
|
||||
# text: String
|
||||
# date: Date
|
||||
# business: Business @relation(name: "REVIEWS", direction: "OUT")
|
||||
# user: User @relation(name: "WROTE", direction: "IN")
|
||||
# }
|
||||
|
||||
# type Category {
|
||||
# name: ID!
|
||||
# businesses: [Business] @relation(name: "IN_CATEGORY", direction: "IN")
|
||||
# }
|
||||
|
||||
# type Query {
|
||||
# usersBySubstring(substring: String): [User]
|
||||
# @cypher(
|
||||
# statement: "MATCH (u:User) WHERE u.name CONTAINS $substring RETURN u"
|
||||
# )
|
||||
# }
|
||||
21
api/src/seed/seed-db.js
Normal file
21
api/src/seed/seed-db.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import ApolloClient from "apollo-client";
|
||||
import gql from "graphql-tag";
|
||||
import dotenv from "dotenv";
|
||||
import seedmutations from "./seed-mutations";
|
||||
import fetch from "node-fetch";
|
||||
import { HttpLink } from "apollo-link-http";
|
||||
import { InMemoryCache } from "apollo-cache-inmemory";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const client = new ApolloClient({
|
||||
link: new HttpLink({ uri: process.env.GRAPHQL_URI, fetch }),
|
||||
cache: new InMemoryCache()
|
||||
});
|
||||
|
||||
client
|
||||
.mutate({
|
||||
mutation: gql(seedmutations)
|
||||
})
|
||||
.then(data => console.log(data))
|
||||
.catch(error => console.error(error));
|
||||
394
api/src/seed/seed-mutations.js
Normal file
394
api/src/seed/seed-mutations.js
Normal file
@@ -0,0 +1,394 @@
|
||||
export default /* GraphQL */ `
|
||||
mutation {
|
||||
u1: CreateUser(id: "u1", name: "Will") {
|
||||
id
|
||||
name
|
||||
}
|
||||
u2: CreateUser(id: "u2", name: "Bob") {
|
||||
id
|
||||
name
|
||||
}
|
||||
u3: CreateUser(id: "u3", name: "Jenny") {
|
||||
id
|
||||
name
|
||||
}
|
||||
u4: CreateUser(id: "u4", name: "Angie") {
|
||||
id
|
||||
name
|
||||
}
|
||||
b1: CreateBusiness(
|
||||
id: "b1"
|
||||
name: "KettleHouse Brewing Co."
|
||||
address: "313 N 1st St W"
|
||||
city: "Missoula"
|
||||
state: "MT"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b2: CreateBusiness(
|
||||
id: "b2"
|
||||
name: "Imagine Nation Brewing"
|
||||
address: "1151 W Broadway St"
|
||||
city: "Missoula"
|
||||
state: "MT"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b3: CreateBusiness(
|
||||
id: "b3"
|
||||
name: "Ninja Mike's"
|
||||
address: "Food Truck - Farmers Market"
|
||||
city: "Missoula"
|
||||
state: "MT"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b4: CreateBusiness(
|
||||
id: "b4"
|
||||
name: "Market on Front"
|
||||
address: "201 E Front St"
|
||||
city: "Missoula"
|
||||
state: "MT"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b5: CreateBusiness(
|
||||
id: "b5"
|
||||
name: "Missoula Public Library"
|
||||
address: "301 E Main St"
|
||||
city: "Missoula"
|
||||
state: "MT"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b6: CreateBusiness(
|
||||
id: "b6"
|
||||
name: "Zootown Brew"
|
||||
address: "121 W Broadway St"
|
||||
city: "Missoula"
|
||||
state: "MT"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b7: CreateBusiness(
|
||||
id: "b7"
|
||||
name: "Hanabi"
|
||||
address: "723 California Dr"
|
||||
city: "Burlingame"
|
||||
state: "CA"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b8: CreateBusiness(
|
||||
id: "b8"
|
||||
name: "Philz Coffee"
|
||||
address: "113 B St"
|
||||
city: "San Mateo"
|
||||
state: "CA"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b9: CreateBusiness(
|
||||
id: "b9"
|
||||
name: "Alpha Acid Brewing Company"
|
||||
address: "121 Industrial Rd #11"
|
||||
city: "Belmont"
|
||||
state: "CA"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
b10: CreateBusiness(
|
||||
id: "b10"
|
||||
name: "San Mateo Public Library Central Library"
|
||||
address: "55 W 3rd Ave"
|
||||
city: "San Mateo"
|
||||
state: "CA"
|
||||
) {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
c1: CreateCategory(name: "Coffee") {
|
||||
name
|
||||
}
|
||||
c2: CreateCategory(name: "Library") {
|
||||
name
|
||||
}
|
||||
c3: CreateCategory(name: "Beer") {
|
||||
name
|
||||
}
|
||||
c4: CreateCategory(name: "Restaurant") {
|
||||
name
|
||||
}
|
||||
c5: CreateCategory(name: "Ramen") {
|
||||
name
|
||||
}
|
||||
c6: CreateCategory(name: "Cafe") {
|
||||
name
|
||||
}
|
||||
c7: CreateCategory(name: "Deli") {
|
||||
name
|
||||
}
|
||||
c8: CreateCategory(name: "Breakfast") {
|
||||
name
|
||||
}
|
||||
c9: CreateCategory(name: "Brewery") {
|
||||
name
|
||||
}
|
||||
|
||||
a1: AddBusinessCategories(from: { id: "b1" }, to: { name: "Beer" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a1a: AddBusinessCategories(from: { id: "b1" }, to: { name: "Brewery" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a2: AddBusinessCategories(from: { id: "b2" }, to: { name: "Beer" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a2a: AddBusinessCategories(from: { id: "b2" }, to: { name: "Brewery" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a3: AddBusinessCategories(from: { id: "b3" }, to: { name: "Restaurant" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a4: AddBusinessCategories(from: { id: "b3" }, to: { name: "Breakfast" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a5: AddBusinessCategories(from: { id: "b4" }, to: { name: "Coffee" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a5a: AddBusinessCategories(from: { id: "b4" }, to: { name: "Restaurant" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a5b: AddBusinessCategories(from: { id: "b4" }, to: { name: "Cafe" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a5c: AddBusinessCategories(from: { id: "b4" }, to: { name: "Deli" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a5d: AddBusinessCategories(from: { id: "b4" }, to: { name: "Breakfast" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a6: AddBusinessCategories(from: { id: "b5" }, to: { name: "Library" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a7: AddBusinessCategories(from: { id: "b6" }, to: { name: "Coffee" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a8: AddBusinessCategories(from: { id: "b7" }, to: { name: "Restaurant" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a8a: AddBusinessCategories(from: { id: "b7" }, to: { name: "Ramen" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a9: AddBusinessCategories(from: { id: "b8" }, to: { name: "Coffee" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a9a: AddBusinessCategories(from: { id: "b8" }, to: { name: "Breakfast" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a10: AddBusinessCategories(from: { id: "b9" }, to: { name: "Brewery" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
a11: AddBusinessCategories(from: { id: "b10" }, to: { name: "Library" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r1: CreateReview(id: "r1", stars: 4, text: "Great IPA selection!", date: { formatted: "2016-01-03"}) {
|
||||
id
|
||||
}
|
||||
ar1: AddUserReviews(from: { id: "u1" }, to: { id: "r1" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab1: AddReviewBusiness(from: { id: "r1" }, to: { id: "b1" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r2: CreateReview(id: "r2", stars: 5, text: "", date: { formatted: "2016-07-14"}) {
|
||||
id
|
||||
}
|
||||
ar2: AddUserReviews(from: { id: "u3" }, to: { id: "r2" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab2: AddReviewBusiness(from: { id: "r2" }, to: { id: "b1" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r3: CreateReview(id: "r3", stars: 3, text: "", date: { formatted: "2018-09-10"}) {
|
||||
id
|
||||
}
|
||||
ar3: AddUserReviews(from: { id: "u4" }, to: { id: "r3" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab3: AddReviewBusiness(from: { id: "r3" }, to: { id: "b2" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r4: CreateReview(id: "r4", stars: 5, text: "", date: { formatted: "2017-11-13"}) {
|
||||
id
|
||||
}
|
||||
ar4: AddUserReviews(from: { id: "u3" }, to: { id: "r4" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab4: AddReviewBusiness(from: { id: "r4" }, to: { id: "b3" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r5: CreateReview(
|
||||
id: "r5"
|
||||
stars: 4
|
||||
text: "Best breakfast sandwich at the Farmer's Market. Always get the works."
|
||||
date: { formatted: "2018-01-03"}
|
||||
) {
|
||||
id
|
||||
}
|
||||
ar5: AddUserReviews(from: { id: "u1" }, to: { id: "r5" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab5: AddReviewBusiness(from: { id: "r5" }, to: { id: "b3" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r6: CreateReview(id: "r6", stars: 4, text: "", date: { formatted: "2018-03-24"}) {
|
||||
id
|
||||
}
|
||||
ar6: AddUserReviews(from: { id: "u2" }, to: { id: "r6" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab6: AddReviewBusiness(from: { id: "r6" }, to: { id: "b4" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r7: CreateReview(
|
||||
id: "r7"
|
||||
stars: 3
|
||||
text: "Not a great selection of books, but fortunately the inter-library loan system is good. Wifi is quite slow. Not many comfortable places to site and read. Looking forward to the new building across the street in 2020!"
|
||||
date: { formatted: "2015-08-29"}
|
||||
) {
|
||||
id
|
||||
}
|
||||
ar7: AddUserReviews(from: { id: "u1" }, to: { id: "r7" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab7: AddReviewBusiness(from: { id: "r7" }, to: { id: "b5" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r8: CreateReview(id: "r8", stars: 5, text: "", date: { formatted: "2018-08-11"}) {
|
||||
id
|
||||
}
|
||||
ar8: AddUserReviews(from: { id: "u4" }, to: { id: "r8" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab8: AddReviewBusiness(from: { id: "r8" }, to: { id: "b6" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r9: CreateReview(id: "r9", stars: 5, text: "", date: { formatted: "2016-11-21"}) {
|
||||
id
|
||||
}
|
||||
ar9: AddUserReviews(from: { id: "u3" }, to: { id: "r9" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab9: AddReviewBusiness(from: { id: "r9" }, to: { id: "b7" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
r10: CreateReview(id: "r10", stars: 4, text: "", date: { formatted: "2015-12-15"}) {
|
||||
id
|
||||
}
|
||||
ar10: AddUserReviews(from: { id: "u2" }, to: { id: "r10" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
ab10: AddReviewBusiness(from: { id: "r10" }, to: { id: "b2" }) {
|
||||
from {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
Reference in New Issue
Block a user