├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README-DEV.md
├── README.md
├── api
├── .dockerignore
├── .env
├── Dockerfile
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── graphql-schema.js
│ ├── index.js
│ └── schema.graphql
├── app.json
├── docker-compose-stage.yml
├── docker-compose.yml
├── img
├── app-browser.jpg
├── cypress-test-runner.jpg
├── docker-desktop.jpg
├── graphql-browser.jpg
├── neo4j-browser.jpg
└── vscode-extensions.jpg
├── neo4j
├── Dockerfile
└── README.md
├── test
├── .dockerignore
├── .gitignore
├── Dockerfile
├── cypress.json
├── cypress
│ ├── fixtures
│ │ ├── example.json
│ │ └── seedDb.cypher
│ ├── integration
│ │ └── spec.js
│ ├── plugins
│ │ └── index.js
│ └── support
│ │ ├── commands.js
│ │ └── index.js
├── package-lock.json
└── package.json
└── ui
├── .dockerignore
├── .env
├── .eslintrc.js
├── .gitignore
├── Dockerfile
├── README.md
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── apollo.js
├── client.js
├── components
│ ├── Nav.svelte
│ └── StarRating.svelte
├── routes
│ ├── _error.svelte
│ ├── _layout.svelte
│ ├── about.svelte
│ ├── categories
│ │ ├── [name].svelte
│ │ └── index.svelte
│ ├── index.svelte
│ └── reviews.svelte
├── server.js
├── service-worker.js
└── template.html
└── static
├── favicon.png
├── global.css
├── great-success.png
├── logo-192.png
├── logo-512.png
└── manifest.json
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Cypress Tests
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-16.04
8 | timeout-minutes: 10
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Neo4j server
12 | run: docker-compose up -d --build
13 | - name: API server
14 | uses: actions/setup-node@v1
15 | with:
16 | node-version: "12.x"
17 | - run: |
18 | npm ci
19 | npm run build
20 | npm run start &
21 | working-directory: api
22 | - name: UI server
23 | uses: actions/setup-node@v1
24 | with:
25 | node-version: "12.x"
26 | - run: |
27 | npm ci
28 | npm run build
29 | npm run start &
30 | working-directory: ui
31 | - name: Cypress run
32 | uses: cypress-io/github-action@v1
33 | with:
34 | browser: electron
35 | wait-on: http://localhost:7474
36 | # record: true
37 | working-directory: test
38 | # group: "Default tests"
39 | # env:
40 | # CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
41 | - uses: actions/upload-artifact@v1
42 | if: failure()
43 | with:
44 | name: cypress-videos
45 | path: test/cypress/videos
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 | yarn.lock
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 | *~
23 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "files.associations": {
4 | "style.grass": "css"
5 | }
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 vanbenj
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-DEV.md:
--------------------------------------------------------------------------------
1 | # Setting up a development environment
2 |
3 | These are instructions for setting up a MacOS machine for development.
4 |
5 | ## Homebrew
6 |
7 | We recommemd using Homebrew to aid the install process. You can find the install [here](https://brew.sh/)
8 |
9 | ## Nodejs
10 |
11 | This project uses Node version 12. If you have node installed test which version you're running.
12 |
13 | ```
14 | node -v
15 | ```
16 |
17 | If you're not running version 12 then unlink node.
18 |
19 | ```
20 | brew unlink node
21 | ```
22 |
23 | Install Node 12 the desired version:
24 |
25 | ```
26 | brew install node@12
27 | brew link node@12
28 | ```
29 |
30 | ## Docker
31 |
32 | When developing on the Mac it is a good idea to install [Docker Desktop](https://www.docker.com/products/docker-desktop)
33 |
34 | This greatly simplifies running Docker on the Mac. Once installed you can access the Docker Dashboard to see when containers are running.
35 |
36 |
37 |
38 | ## Vscode
39 |
40 | This was developed using Visuals Studio Code for node development.
41 | Install from [here](https://code.visualstudio.com/)
42 |
43 | In addition we recommend the extensions for Graphql, Neo4j, Svelte and Prettier. The project is configured to automatically format on save and these extensions are necessary to ensure a common formatting is used.
44 |
45 |
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SANDstack Starter
2 |
3 | This project is a starter for building a SANDstack ([Sveltejs](https://svelte.dev/)/[Sapper](https://sapper.svelte.dev/), [Apollo GraphQL](https://www.apollographql.com/), [Neo4j Database](https://neo4j.com/neo4j-graph-database/)) application. There are two components to the starter, the UI application (a Svelte/Sapper app) and the API app (GraphQL server).
4 |
5 | This project used as a starting point the api component from the [GRANDstack](https://grandstack.io) project and the default [Sapper template](https://github.com/sveltejs/sapper-template).
6 |
7 | The master branch **DOES NOT USE** [Svelte Apollo](https://github.com/timhall/svelte-apollo) There is a separate branch that demonstrates it's use.
8 |
9 | If you are new to Svelete this is a good [introductory video](https://youtu.be/AdNJ3fydeao)
10 |
11 | The GRANDStack documentation provides a good overview of the [Neo4j GraphQL](https://grandstack.io/docs/neo4j-graphql-overview.html) integration.
12 |
13 | ## Setting up a development environment
14 |
15 | Follow [these instructions first](./README-DEV.md)
16 |
17 | ## Quickstart
18 |
19 | You can quickly start using Docker engine version 19 or later:
20 |
21 | ```
22 | docker-compose -f docker-compose.yml -f docker-compose-stage.yml up --build
23 | ```
24 |
25 | List the running containers:
26 |
27 | ```
28 | docker ps
29 | ```
30 |
31 | There should be three containers started:
32 |
33 | ```
34 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
35 | 625acfa444fa sand-stack-starter_ui "docker-entrypoint.s…" 3 minutes ago Up 2 minutes 0.0.0.0:3000->3000/tcp sand-stack-starter_ui_1
36 | 843c5ea8d997 sand-stack-starter_api "docker-entrypoint.s…" 3 minutes ago Up 2 minutes 0.0.0.0:4001->4001/tcp sand-stack-starter_api_1
37 | 8ae79170f66e sand-stack-starter_neo4j "/sbin/tini -g -- /d…" 3 minutes ago Up 3 minutes 0.0.0.0:7474->7474/tcp, 7473/tcp, 0.0.0.0:7687->7687/tcp sand-stack-starter_neo4j_1
38 | ```
39 |
40 | Initially the database is empty. Running the test suite automatically loads the database with sample data.
41 |
42 | Open a new terminal
43 |
44 | ```
45 | cd test
46 | npm install
47 | npx cypress run
48 | ```
49 |
50 | The application should be running at [localhost:3000](http://localhost:3000)
51 | 
52 |
53 | ## Development mode
54 |
55 | The project is set up to allow the api and ui servers to run in dev mode. This mode enables a watch on all files and automatically deploys when changes are saved. The following instructions show you how to start each server component separately.
56 |
57 | If you've run the docker-compose script be sure to shut down the docker containers before starting in development mode.
58 |
59 | ```
60 | docker-compose down
61 | ```
62 |
63 | Be sure your development machine is running Node version 12.
64 |
65 | ```
66 | node --version
67 | ```
68 |
69 | ### Neo4j
70 |
71 | There are many ways to run Neo4j for development purposes. The [README](neo4j/README.md) in the `neo4j` directory describes several approaches. However the simplest is to start a stand alone Docker container using the DockerFile provided.
72 |
73 | ```
74 | docker-compose up --detach --remove-orphans
75 | ```
76 |
77 | List the running containers:
78 |
79 | ```
80 | docker ps
81 | ```
82 |
83 | You should see a neo4j container:
84 |
85 | ```
86 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
87 | 3c95a2947d49 neo4j "/sbin/tini -g -- /d…" 9 seconds ago Up 8 seconds 0.0.0.0:7474->7474/tcp, 7473/tcp, 0.0.0.0:7687->7687/tcp neo4j-db
88 | ```
89 |
90 | You should now be able to access the Neo4j database browser at [localhost:7474](http://localhost:7474) (you can log in using neo4j/letmein). Click the `*()` under `Node Labels` to list a sample of nodes.
91 | 
92 |
93 | ### Install dependencies
94 |
95 | ```
96 | (cd ../ui && npm install)
97 | (cd ../api && npm install)
98 | ```
99 |
100 | ### Start API Server
101 |
102 | Open a new terminal
103 |
104 | ```
105 | cd api
106 | npm run dev
107 | ```
108 |
109 | This will start the GraphQL API server in dev mode.
110 |
111 | You should also be able to access the Apollo GraphQL playground at [localhost:4001/graphql](http://localhost:4001/graphql) This allows you to interactively test your GraphQL before using it in your application.
112 | 
113 |
114 | ### Start UI Server
115 |
116 | Open a new terminal
117 |
118 | ```
119 | cd ui
120 | npm run dev
121 | ```
122 |
123 | This will start the Svelte Sapper app in dev mode. In dev mode when a file change is saved in the `ui` directory the web page should automatically reload with the changes.
124 |
125 | ### Start Cypress Test Server
126 |
127 | Open a new terminal
128 |
129 | ```
130 | cd test
131 | npx cypress open
132 | ```
133 |
134 | The test runner lets you select the browser you wish to use for testing.
135 | 
136 |
137 | We've found as as apps get larger and more complex it is often necessay to add [cy.wait(time)](https://docs.cypress.io/api/commands/wait.html#Syntax) to make the test stable on slower CI machines. These are end to end tests so many factors can affect test performance.
138 |
139 | ### Github Continuous Integration
140 |
141 | The `/.github/workflows/test.yml` is a Github action that runs the Cypress end to end tests and stores the test results to github on every push.
142 |
143 | ### Sapper Export
144 |
145 | This simple example is a readonly app and a candidate for [Sapper Export](https://sapper.svelte.dev/docs#Exporting)
146 |
147 | For the export to run properly the `neo4j` and `api` servers must be running.
148 |
149 | Open a new terminal
150 |
151 | ```
152 | cd ui
153 | npx sapper export
154 | ```
155 |
156 | This will create a `/ui/__sapper__/export` folder with a production-ready build of your site. You can launch it like so:
157 |
158 | ```
159 | npx serve __sapper__/export
160 | ```
161 |
162 | ### Sapper Environment
163 |
164 | The `ui` app makes use of the [Sapper Environment npm](https://www.npmjs.com/package/sapper-environment). These are environment variables that are replaced at build time by `rollup.config.js` so they can be used in client code. Any environment variable with prefix `SAPPER_APP_` is for use in the client code.
165 |
166 | We make especial use of this in `/ui/src/apollo.js`. The Apollo GraphQl client is used in both the browser and SSR. In dev mode they have the same value.
167 |
168 | ```
169 | SSR_GRAPHQL_URI=http://localhost:4001/graphql
170 | SAPPER_APP_GRAPHQL_URI=http://localhost:4001/graphql
171 | ```
172 |
173 | However in production they are often different as the `SAPPER_APP_GRAPHQL_URI` is the public address of the api server and `SSR_GRAPHQL_URI` is the internal address of the server. An example of this can be seen in `docker-compose-stage.yml`.
174 |
--------------------------------------------------------------------------------
/api/.dockerignore:
--------------------------------------------------------------------------------
1 | *~
2 |
--------------------------------------------------------------------------------
/api/.env:
--------------------------------------------------------------------------------
1 | NODE_ENV=development
2 | NEO4J_URI=bolt://localhost:7687
3 | NEO4J_USER=neo4j
4 | NEO4J_PASSWORD=letmein
5 | GRAPHQL_LISTEN_PORT=4001
6 | GRAPHQL_URI=http://localhost:4001/graphql
7 |
--------------------------------------------------------------------------------
/api/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:12-alpine
2 |
3 | RUN mkdir -p /app
4 | WORKDIR /app
5 |
6 | COPY package.json .
7 | COPY package-lock.json .
8 | RUN npm install
9 | COPY src ./src
10 | COPY .env .
11 |
12 | EXPOSE 4001
13 |
14 | CMD ["npm", "start"]
15 |
--------------------------------------------------------------------------------
/api/README.md:
--------------------------------------------------------------------------------
1 | # GRANDstack Starter - GraphQL API
2 |
3 |
4 | ## Quick Start
5 |
6 | Install dependencies:
7 |
8 | ```
9 | npm install
10 | ```
11 |
12 | Start the GraphQL service:
13 |
14 | ```
15 | npm start
16 | ```
17 |
18 | This will start the GraphQL service (by default on localhost:4001/graphql) where you can issue GraphQL requests or access GraphQL Playground in the browser:
19 |
20 | 
21 |
22 | ## Configure
23 |
24 | Set your Neo4j connection string and credentials in `.env`. For example:
25 |
26 | *.env*
27 |
28 | ```
29 | NEO4J_URI=bolt://localhost:7687
30 | NEO4J_USER=neo4j
31 | NEO4J_PASSWORD=letmein
32 | ```
33 |
34 | ## Seeding The Database
35 |
36 | Optionally you can seed the GraphQL service by executing mutations that will write sample data to the database:
37 |
38 | ```
39 | npm run seedDb
40 | ```
41 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sand-stack-starter-api",
3 | "version": "0.1.0",
4 | "description": "API app for SANDstack",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "./node_modules/.bin/nodemon --watch src --ext js,graphql --exec babel-node src/index.js",
9 | "build": "npx babel src -d build; cp .env build; cp src/schema.graphql build",
10 | "start": "npm run build && node build/index.js"
11 | },
12 | "license": "MIT",
13 | "dependencies": {
14 | "apollo-boost": "^0.3.1",
15 | "apollo-cache-inmemory": "^1.6.5",
16 | "apollo-client": "^2.6.8",
17 | "apollo-link-http": "^1.5.17",
18 | "apollo-server": "^2.14.2",
19 | "dotenv": "^8.2.0",
20 | "graphql-tag": "^2.10.3",
21 | "neo4j-driver": "^1.7.6",
22 | "neo4j-graphql-js": "^2.13.0",
23 | "node-fetch": "^2.6.0"
24 | },
25 | "devDependencies": {
26 | "@babel/cli": "^7.8.4",
27 | "@babel/core": "^7.9.0",
28 | "@babel/node": "^7.8.7",
29 | "@babel/preset-env": "^7.9.5",
30 | "nodemon": "^1.19.4",
31 | "prettier": "^1.19.1"
32 | },
33 | "babel": {
34 | "presets": [
35 | [
36 | "@babel/preset-env",
37 | {
38 | "corejs": "3.1.3",
39 | "useBuiltIns": "usage"
40 | }
41 | ]
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/api/src/graphql-schema.js:
--------------------------------------------------------------------------------
1 | import { neo4jgraphql } from "neo4j-graphql-js";
2 | import fs from "fs";
3 | import path from "path";
4 |
5 | /*
6 | * Check for GRAPHQL_SCHEMA environment variable to specify schema file
7 | * fallback to schema.graphql if GRAPHQL_SCHEMA environment variable is not set
8 | */
9 |
10 | export const typeDefs = fs
11 | .readFileSync(
12 | process.env.GRAPHQL_SCHEMA || path.join(__dirname, "schema.graphql")
13 | )
14 | .toString("utf-8");
15 |
16 | export const resolvers = {
17 | // add resolvers that can't be autogenerated by neo4jgraphql
18 | Query: {
19 | async hello(_, args, ctx, info) {
20 | return "Hello World!";
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/api/src/index.js:
--------------------------------------------------------------------------------
1 | import { typeDefs, resolvers } from "./graphql-schema";
2 | import { ApolloServer } from "apollo-server-express";
3 | import express from "express";
4 | import { v1 as neo4j } from "neo4j-driver";
5 | import { makeAugmentedSchema } from "neo4j-graphql-js";
6 | import dotenv from "dotenv";
7 |
8 | // set environment variables from ../.env
9 | dotenv.config();
10 | const dev = process.env.NODE_ENV === "development";
11 |
12 | const app = express();
13 |
14 | /*
15 | * Create an executable GraphQL schema object from GraphQL type definitions
16 | * including autogenerated queries and mutations.
17 | * Optionally a config object can be included to specify which types to include
18 | * in generated queries and/or mutations. Read more in the docs:
19 | * https://grandstack.io/docs/neo4j-graphql-js-api.html#makeaugmentedschemaoptions-graphqlschema
20 | */
21 |
22 | const schema = makeAugmentedSchema({
23 | typeDefs,
24 | resolvers
25 | });
26 |
27 | /*
28 | * Create a Neo4j driver instance to connect to the database
29 | * using credentials specified as environment variables
30 | */
31 | const driver = neo4j.driver(
32 | process.env.NEO4J_URI,
33 | neo4j.auth.basic(process.env.NEO4J_USER, process.env.NEO4J_PASSWORD)
34 | );
35 |
36 | /*
37 | * Create a new ApolloServer instance, serving the GraphQL schema
38 | * created using makeAugmentedSchema above and injecting the Neo4j driver
39 | * instance into the context object so it is available in the
40 | * generated resolvers to connect to the database.
41 | */
42 | const server = new ApolloServer({
43 | context: { driver },
44 | schema: schema,
45 | introspection: dev,
46 | playground: dev
47 | });
48 |
49 | // Specify port and path for GraphQL endpoint
50 | const port = process.env.GRAPHQL_LISTEN_PORT || 4001;
51 | const path = "/graphql";
52 |
53 | /*
54 | * Optionally, apply Express middleware for authentication, etc
55 | * This also also allows us to specify a path for the GraphQL endpoint
56 | */
57 | server.applyMiddleware({ app, path });
58 |
59 | app.listen({ port, path }, () => {
60 | console.log(`GraphQL server ready at http://localhost:${port}${path}`);
61 | });
62 |
--------------------------------------------------------------------------------
/api/src/schema.graphql:
--------------------------------------------------------------------------------
1 | type User {
2 | id: ID!
3 | name: String
4 | friends: [User] @relation(name: "FRIENDS", direction: "BOTH")
5 | reviews: [Review] @relation(name: "WROTE", direction: "OUT")
6 | avgStars: Float
7 | @cypher(
8 | statement: "MATCH (this)-[:WROTE] RETURN toFloat(avg(r.stars))"
9 | )
10 | numReviews: Int
11 | @cypher(statement: "MATCH (this)-[:WROTE]->(r:Review) RETURN COUNT(r)")
12 | 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")
13 | }
14 |
15 | type Business {
16 | id: ID!
17 | name: String
18 | address: String
19 | city: String
20 | state: String
21 | avgStars: Float @cypher(statement: "MATCH (this)<-[:REVIEWS]-(r:Review) RETURN coalesce(avg(r.stars),0.0)")
22 | reviews: [Review] @relation(name: "REVIEWS", direction: "IN")
23 | categories: [Category] @relation(name: "IN_CATEGORY", direction: "OUT")
24 | }
25 |
26 | type Review {
27 | id: ID!
28 | stars: Int
29 | text: String
30 | date: Date
31 | business: Business @relation(name: "REVIEWS", direction: "OUT")
32 | user: User @relation(name: "WROTE", direction: "IN")
33 | }
34 |
35 | type Category {
36 | name: ID!
37 | businesses: [Business] @relation(name: "IN_CATEGORY", direction: "IN")
38 | }
39 |
40 | type Query {
41 | usersBySubstring(substring: String): [User] @cypher(statement: "MATCH (u:User) WHERE u.name CONTAINS $substring RETURN u")
42 | hello: String
43 | }
44 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sand-stack-starter",
3 | "env": {
4 | "NEO4J_URI": {
5 | "description": "The bolt endpoint for your neo4j database. For example: bolt://grandstack.io:7687",
6 | "required": true
7 | },
8 | "NEO4J_USER": {
9 | "description": "The database user that the API application will use to connect to Neo4j",
10 | "required": true
11 | },
12 | "NEO4J_PASSWORD": {
13 | "description": "The password for the provided Neo4j database user",
14 | "required": true
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/docker-compose-stage.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | services:
4 | api:
5 | build: ./api
6 | ports:
7 | - 4001:4001
8 | environment:
9 | - NODE_ENV=staging
10 | - GRAPHQL_LISTEN_PORT=4001
11 | - GRAPHQL_URI=http://api:4001/graphql
12 | - NEO4J_URI=bolt://neo4j:7687
13 | - NEO4J_USER=neo4j
14 | - NEO4J_PASSWORD=letmein
15 | links:
16 | - neo4j
17 | depends_on:
18 | - neo4j
19 | ui:
20 | build:
21 | context: ./ui
22 | args:
23 | - SAPPER_APP_GRAPHQL_URI=http://localhost:4001/graphql
24 | - NODE_ENV=staging
25 | ports:
26 | - 3000:3000
27 | environment:
28 | - NODE_ENV=staging
29 | - SSR_GRAPHQL_URI=http://api:4001/graphql
30 | links:
31 | - api
32 | depends_on:
33 | - api
34 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | services:
4 | neo4j:
5 | build: ./neo4j
6 | ports:
7 | - 7474:7474
8 | - 7687:7687
9 | environment:
10 | - NEO4J_dbms_security_procedures_unrestricted=apoc.*
11 | - NEO4J_apoc_import_file_enabled=true
12 | - NEO4J_apoc_export_file_enabled=true
13 | - NEO4J_dbms_shell_enabled=true
14 |
--------------------------------------------------------------------------------
/img/app-browser.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanbenj/sand-stack-starter/e18208286f9cb45a97aa33984ed20ae735eff805/img/app-browser.jpg
--------------------------------------------------------------------------------
/img/cypress-test-runner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanbenj/sand-stack-starter/e18208286f9cb45a97aa33984ed20ae735eff805/img/cypress-test-runner.jpg
--------------------------------------------------------------------------------
/img/docker-desktop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanbenj/sand-stack-starter/e18208286f9cb45a97aa33984ed20ae735eff805/img/docker-desktop.jpg
--------------------------------------------------------------------------------
/img/graphql-browser.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanbenj/sand-stack-starter/e18208286f9cb45a97aa33984ed20ae735eff805/img/graphql-browser.jpg
--------------------------------------------------------------------------------
/img/neo4j-browser.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanbenj/sand-stack-starter/e18208286f9cb45a97aa33984ed20ae735eff805/img/neo4j-browser.jpg
--------------------------------------------------------------------------------
/img/vscode-extensions.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanbenj/sand-stack-starter/e18208286f9cb45a97aa33984ed20ae735eff805/img/vscode-extensions.jpg
--------------------------------------------------------------------------------
/neo4j/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM neo4j:3.5.12
2 |
3 | ENV NEO4J_AUTH=neo4j/letmein \
4 | APOC_VERSION=3.5.0.5 \
5 | GRAPHQL_VERSION=3.5.0.4
6 |
7 | ENV APOC_URI https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases/download/${APOC_VERSION}/apoc-${APOC_VERSION}-all.jar
8 | RUN sh -c 'cd /var/lib/neo4j/plugins && curl -L -O "${APOC_URI}"'
9 |
10 | ENV GRAPHQL_URI https://github.com/neo4j-graphql/neo4j-graphql/releases/download/${GRAPHQL_VERSION}/neo4j-graphql-${GRAPHQL_VERSION}.jar
11 | RUN sh -c 'cd /var/lib/neo4j/plugins && curl -L -O "${GRAPHQL_URI}"'
12 |
13 | EXPOSE 7474 7473 7687
14 |
15 | CMD ["neo4j"]
16 |
--------------------------------------------------------------------------------
/neo4j/README.md:
--------------------------------------------------------------------------------
1 | # Neo4j
2 |
3 | You need a Neo4j instance, e.g. a [Neo4j Sandbox](http://neo4j.com/sandbox), a local instance via [Neo4j Desktop](https://neo4j.com/download), [Docker](http://hub.docker.com/_/neo4j) or a [Neo4j instance on AWS, Azure or GCP](http://neo4j.com/developer/guide-cloud-deployment) or [Neo4j Cloud](http://neo4j.com/cloud)
4 |
5 | For schemas using the `@cypher` directive (as in this repo) via [`neo4j-graphql-js`](https://github.com/neo4j-graphql/neo4j-graphql-js), you need to have the [APOC library](https://github.com/neo4j-contrib/neo4j-apoc-procedures) installed, which should be automatic in Sandbox, Cloud and is a single click install in Neo4j Desktop. If when using the Sandbox / cloud you encounter an issue where an error similar to `Can not be converted to long: org.neo4j.kernel.impl.core.NodeProxy, Location: [object Object], Path: users` appears in the console when running the ui app, try installing and using Neo4j locally instead.
6 |
7 | ## Sandbox setup
8 | A good tutorial can be found here: https://www.youtube.com/watch?v=rPC71lUhK_I
9 |
10 | ## Local setup
11 | 1. [Download Neo4j Desktop](https://neo4j.com/download/)
12 | 2. Install and open Neo4j Desktop.
13 | 3. Create a new DB by clicking "New Graph", and clicking "create local graph".
14 | 4. Set password to "letmein" (as suggested by `api/.env`), and click "Create".
15 | 5. Make sure that the default credentials in `api/.env` are used. Leave them as follows: `NEO4J_URI=bolt://localhost:7687 NEO4J_USER=neo4j NEO4J_PASSWORD=letmein`
16 | 6. Click "Manage".
17 | 7. Click "Plugins".
18 | 8. Find "APOC" and click "Install".
19 | 9. Click the "play" button at the top of left the screen, which should start the server. _(screenshot 2)_
20 | 10. Wait until it says "RUNNING".
21 | 11. Proceed forward with the rest of the tutorial.
22 |
23 |
--------------------------------------------------------------------------------
/test/.dockerignore:
--------------------------------------------------------------------------------
1 | .env
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules/
3 | /cypress/screenshots/
4 | /cypress/videos/
5 |
--------------------------------------------------------------------------------
/test/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM cypress/base:12.1.0
2 |
3 | RUN mkdir -p /app
4 | WORKDIR /app
5 |
6 | COPY package.json .
7 | COPY package-lock.json .
8 | COPY cypress.json .
9 | COPY cypress ./cypress
10 |
11 | # by setting CI environment variable we switch the Cypress install messages
12 | # to small "started / finished" and avoid 1000s of lines of progress messages
13 | # https://github.com/cypress-io/cypress/issues/1243
14 | ENV CI=1
15 | RUN npm ci
16 | # verify that Cypress has been installed correctly.
17 | # running this command separately from "cypress run" will also cache its result
18 | # to avoid verifying again when running the tests
19 | RUN npx cypress verify
--------------------------------------------------------------------------------
/test/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:3000",
3 | "video": true,
4 | "chromeWebSecurity": false,
5 | "env": {
6 | "NEO4J_URI": "bolt://localhost:7687",
7 | "NEO4J_USER": "neo4j",
8 | "NEO4J_PASSWORD": "letmein"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/test/cypress/fixtures/seedDb.cypher:
--------------------------------------------------------------------------------
1 |
2 | // this script was generated using
3 | // https://neo4j.com/docs/labs/apoc/current/export/cypher/#export-cypher-neo4j-browser
4 | // you can run the command using http://localhost:7474/browser/
5 | // after the command is run you can use the docker shell
6 | // docker exec -it sand-stack-starter_neo4j_1 sh
7 | // to find the file in the import folder on the neo4j server
8 | CREATE CONSTRAINT ON (node:`UNIQUE IMPORT LABEL`) ASSERT (node.`UNIQUE IMPORT ID`) IS UNIQUE;
9 | UNWIND [{_id:0, properties:{name:"Will", id:"u1"}}, {_id:1, properties:{name:"Bob", id:"u2"}}, {_id:2, properties:{name:"Jenny", id:"u3"}}, {_id:3, properties:{name:"Angie", id:"u4"}}] AS row
10 | CREATE (n:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row._id}) SET n += row.properties SET n:User;
11 | UNWIND [{_id:21, properties:{name:"Coffee"}}, {_id:22, properties:{name:"Library"}}, {_id:23, properties:{name:"Beer"}}, {_id:24, properties:{name:"Restaurant"}}, {_id:25, properties:{name:"Ramen"}}, {_id:26, properties:{name:"Cafe"}}, {_id:27, properties:{name:"Deli"}}, {_id:28, properties:{name:"Breakfast"}}, {_id:29, properties:{name:"Brewery"}}] AS row
12 | CREATE (n:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row._id}) SET n += row.properties SET n:Category;
13 | UNWIND [{_id:30, properties:{date:date('2016-01-03'), text:"Great IPA selection!", id:"r1", stars:4}}, {_id:31, properties:{date:date('2016-07-14'), text:"I love everything", id:"r2", stars:5}}, {_id:32, properties:{date:date('2018-09-10'), text:"It's lacking something", id:"r3", stars:3}}, {_id:33, properties:{date:date('2017-11-13'), text:"Love it more", id:"r4", stars:5}}, {_id:34, properties:{date:date('2018-01-03'), text:"Best breakfast sandwich at the Farmer's Market. Always get the works.", id:"r5", stars:4}}, {_id:35, properties:{date:date('2018-03-24'), text:"Uses mostly organic food, vegan friendly", id:"r6", stars:4}}, {_id:36, properties:{date:date('2015-08-29'), text:"Not a great selection of books, but fortunately the inter-library loan system is good. Wifi is quite slow.", id:"r7", stars:3}}, {_id:37, properties:{date:date('2018-08-11'), text:"Zebra pale ale is nice", id:"r8", stars:5}}, {_id:38, properties:{date:date('2016-11-21'), text:"Love it!!", id:"r9", stars:5}}, {_id:39, properties:{date:date('2015-12-15'), text:"Somewhat lacking in imagination", id:"r10", stars:2}}] AS row
14 | CREATE (n:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row._id}) SET n += row.properties SET n:Review;
15 | UNWIND [{_id:4, properties:{address:"313 N 1st St W", city:"Missoula", name:"KettleHouse Brewing Co.", state:"MT", id:"b1"}}, {_id:5, properties:{address:"1151 W Broadway St", city:"Missoula", name:"Imagine Nation Brewing", state:"MT", id:"b2"}}, {_id:6, properties:{address:"Food Truck - Farmers Market", city:"Missoula", name:"Ninja Mike's", id:"b3", state:"MT"}}, {_id:7, properties:{address:"201 E Front St", city:"Missoula", name:"Market on Front", state:"MT", id:"b4"}}, {_id:8, properties:{address:"301 E Main St", city:"Missoula", name:"Missoula Public Library", state:"MT", id:"b5"}}, {_id:9, properties:{address:"121 W Broadway St", city:"Missoula", name:"Zootown Brew", id:"b6", state:"MT"}}, {_id:10, properties:{address:"723 California Dr", city:"Burlingame", name:"Hanabi", id:"b7", state:"CA"}}, {_id:11, properties:{address:"113 B St", city:"San Mateo", name:"Philz Coffee", id:"b8", state:"CA"}}, {_id:12, properties:{address:"121 Industrial Rd #11", city:"Belmont", name:"Alpha Acid Brewing Company", id:"b9", state:"CA"}}, {_id:20, properties:{address:"55 W 3rd Ave", city:"San Mateo", name:"San Mateo Public Library Central Library", state:"CA", id:"b10"}}] AS row
16 | CREATE (n:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row._id}) SET n += row.properties SET n:Business;
17 | UNWIND [{start: {_id:0}, end: {_id:30}, properties:{}}, {start: {_id:2}, end: {_id:31}, properties:{}}, {start: {_id:3}, end: {_id:32}, properties:{}}, {start: {_id:2}, end: {_id:33}, properties:{}}, {start: {_id:0}, end: {_id:34}, properties:{}}, {start: {_id:1}, end: {_id:35}, properties:{}}, {start: {_id:0}, end: {_id:36}, properties:{}}, {start: {_id:3}, end: {_id:37}, properties:{}}, {start: {_id:2}, end: {_id:38}, properties:{}}, {start: {_id:1}, end: {_id:39}, properties:{}}] AS row
18 | MATCH (start:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row.start._id})
19 | MATCH (end:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row.end._id})
20 | CREATE (start)-[r:WROTE]->(end) SET r += row.properties;
21 | UNWIND [{start: {_id:4}, end: {_id:29}, properties:{}}, {start: {_id:5}, end: {_id:23}, properties:{}}, {start: {_id:5}, end: {_id:29}, properties:{}}, {start: {_id:6}, end: {_id:24}, properties:{}}, {start: {_id:6}, end: {_id:28}, properties:{}}, {start: {_id:7}, end: {_id:21}, properties:{}}, {start: {_id:7}, end: {_id:24}, properties:{}}, {start: {_id:7}, end: {_id:26}, properties:{}}, {start: {_id:7}, end: {_id:27}, properties:{}}, {start: {_id:7}, end: {_id:28}, properties:{}}, {start: {_id:8}, end: {_id:22}, properties:{}}, {start: {_id:9}, end: {_id:21}, properties:{}}, {start: {_id:10}, end: {_id:24}, properties:{}}, {start: {_id:10}, end: {_id:25}, properties:{}}, {start: {_id:11}, end: {_id:21}, properties:{}}, {start: {_id:11}, end: {_id:28}, properties:{}}, {start: {_id:12}, end: {_id:29}, properties:{}}, {start: {_id:20}, end: {_id:22}, properties:{}}, {start: {_id:4}, end: {_id:23}, properties:{}}] AS row
22 | MATCH (start:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row.start._id})
23 | MATCH (end:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row.end._id})
24 | CREATE (start)-[r:IN_CATEGORY]->(end) SET r += row.properties;
25 | UNWIND [{start: {_id:30}, end: {_id:4}, properties:{}}, {start: {_id:31}, end: {_id:4}, properties:{}}, {start: {_id:32}, end: {_id:5}, properties:{}}, {start: {_id:33}, end: {_id:6}, properties:{}}, {start: {_id:34}, end: {_id:6}, properties:{}}, {start: {_id:35}, end: {_id:7}, properties:{}}, {start: {_id:36}, end: {_id:8}, properties:{}}, {start: {_id:37}, end: {_id:9}, properties:{}}, {start: {_id:38}, end: {_id:10}, properties:{}}, {start: {_id:39}, end: {_id:5}, properties:{}}] AS row
26 | MATCH (start:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row.start._id})
27 | MATCH (end:`UNIQUE IMPORT LABEL`{`UNIQUE IMPORT ID`: row.end._id})
28 | CREATE (start)-[r:REVIEWS]->(end) SET r += row.properties;
29 | MATCH (n:`UNIQUE IMPORT LABEL`) WITH n LIMIT 20000 REMOVE n:`UNIQUE IMPORT LABEL` REMOVE n.`UNIQUE IMPORT ID`;
30 | DROP CONSTRAINT ON (node:`UNIQUE IMPORT LABEL`) ASSERT (node.`UNIQUE IMPORT ID`) IS UNIQUE;
--------------------------------------------------------------------------------
/test/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | describe("Sapper template app", () => {
2 | before(() => {
3 | cy.seedDb();
4 | });
5 |
6 | it("has the correct
{error.message}
38 | 39 | {#if dev && error.stack} 40 |{error.stack}41 | {/if} 42 | -------------------------------------------------------------------------------- /ui/src/routes/_layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | 20 |
This is the 'about' page. There's not much here.
8 | -------------------------------------------------------------------------------- /ui/src/routes/categories/[name].svelte: -------------------------------------------------------------------------------- 1 | 34 | 35 | 41 | 42 | 52 | 53 |49 | 50 | Try editing this file (src/routes/index.svelte) to test live reloading. 51 | 52 |
53 | -------------------------------------------------------------------------------- /ui/src/routes/reviews.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 | 39 | 40 | 46 | 47 |