From 8f41a6ceee5ca37f81e84dc8c99d7b06dcba77c5 Mon Sep 17 00:00:00 2001
From: Guillaume Schurck <g.schurck@gmail.com>
Date: Sat, 24 Sep 2022 16:10:12 +0200
Subject: [PATCH] add fastapi backend

---
 sg-backend/.gitignore                         |  1 +
 sg-backend/.idea/.gitignore                   |  8 ++++
 sg-backend/.idea/discord.xml                  |  7 +++
 .../inspectionProfiles/Project_Default.xml    | 12 +++++
 .../inspectionProfiles/profiles_settings.xml  |  6 +++
 sg-backend/.idea/misc.xml                     |  4 ++
 sg-backend/.idea/modules.xml                  |  8 ++++
 sg-backend/.idea/sg-backend.iml               |  8 ++++
 sg-backend/.idea/vcs.xml                      |  6 +++
 sg-backend/db.py                              | 32 ++++++++++++-
 sg-backend/main.py                            | 47 ++++++++++++++++++-
 sg-backend/poetry.lock                        | 28 ++++++++++-
 sg-backend/pyproject.toml                     |  5 +-
 sg-backend/test_main.http                     |  7 ++-
 14 files changed, 172 insertions(+), 7 deletions(-)
 create mode 100644 sg-backend/.idea/.gitignore
 create mode 100644 sg-backend/.idea/discord.xml
 create mode 100644 sg-backend/.idea/inspectionProfiles/Project_Default.xml
 create mode 100644 sg-backend/.idea/inspectionProfiles/profiles_settings.xml
 create mode 100644 sg-backend/.idea/misc.xml
 create mode 100644 sg-backend/.idea/modules.xml
 create mode 100644 sg-backend/.idea/sg-backend.iml
 create mode 100644 sg-backend/.idea/vcs.xml

diff --git a/sg-backend/.gitignore b/sg-backend/.gitignore
index e69de29..5d26889 100644
--- a/sg-backend/.gitignore
+++ b/sg-backend/.gitignore
@@ -0,0 +1 @@
+__pycache/
\ No newline at end of file
diff --git a/sg-backend/.idea/.gitignore b/sg-backend/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/sg-backend/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/sg-backend/.idea/discord.xml b/sg-backend/.idea/discord.xml
new file mode 100644
index 0000000..d8e9561
--- /dev/null
+++ b/sg-backend/.idea/discord.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="DiscordProjectSettings">
+    <option name="show" value="PROJECT_FILES" />
+    <option name="description" value="" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/sg-backend/.idea/inspectionProfiles/Project_Default.xml b/sg-backend/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..9d56344
--- /dev/null
+++ b/sg-backend/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,12 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="ignoredIdentifiers">
+        <list>
+          <option value="ratelimit.decorators.ratelimit.ALL" />
+        </list>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/sg-backend/.idea/inspectionProfiles/profiles_settings.xml b/sg-backend/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/sg-backend/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>
\ No newline at end of file
diff --git a/sg-backend/.idea/misc.xml b/sg-backend/.idea/misc.xml
new file mode 100644
index 0000000..cd8c9b6
--- /dev/null
+++ b/sg-backend/.idea/misc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Poetry (sg-backend)" project-jdk-type="Python SDK" />
+</project>
\ No newline at end of file
diff --git a/sg-backend/.idea/modules.xml b/sg-backend/.idea/modules.xml
new file mode 100644
index 0000000..be6a241
--- /dev/null
+++ b/sg-backend/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/sg-backend.iml" filepath="$PROJECT_DIR$/.idea/sg-backend.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/sg-backend/.idea/sg-backend.iml b/sg-backend/.idea/sg-backend.iml
new file mode 100644
index 0000000..d0876a7
--- /dev/null
+++ b/sg-backend/.idea/sg-backend.iml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/sg-backend/.idea/vcs.xml b/sg-backend/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/sg-backend/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/sg-backend/db.py b/sg-backend/db.py
index 1193b69..51d91b3 100644
--- a/sg-backend/db.py
+++ b/sg-backend/db.py
@@ -1,7 +1,7 @@
 from neo4j import GraphDatabase
 
 
-class HelloWorldExample:
+class Neo4j:
 
     def __init__(self, uri, user, password):
         self.driver = GraphDatabase.driver(uri, auth=(user, password))
@@ -19,4 +19,32 @@ class HelloWorldExample:
         result = tx.run("CREATE (a:Greeting) "
                         "SET a.message = $message "
                         "RETURN a.message + ', from node ' + id(a)", message=message)
-        return result.single()[0]
\ No newline at end of file
+        return result.single()[0]
+
+    def get_graph_data(self):
+        with self.driver.session() as session:
+            # results = session.run("MATCH (n)-[r]->(m) RETURN n,r,m")
+            results = session.run("""CALL apoc.export.json.all(null,{useTypes:true, stream: true, jsonFormat: "JSON"})
+                                        YIELD data
+                                        RETURN data""")
+            return results.data()
+
+    def get_softwares(self):
+        with self.driver.session() as session:
+            results = session.run("MATCH (n:Software) RETURN n")
+            return results.data()
+
+    def get_softwares_shortest_path(self, search):
+        with self.driver.session() as session:
+            # results = session.run("""MATCH (n:Software)-[r:DEPENDS_ON*]-(m:Software)
+            #                             WHERE n.name = $software1 AND m.name = $software2
+            #                             RETURN n,r,m""", software1=SoftwareSearch.software1,
+            #                       software2=SoftwareSearch.software2)
+            results = session.run("""MATCH (S1:Software {name: $software1}), (S2:Software {name: $software2}),
+                                      p = shortestPath((S1)-[*]-(S2))
+                                        RETURN p""",
+                                  software1=search.software1,
+                                  software2=search.software2)
+            data = results.data()[0]['p']
+            return [node for node in data if
+                    type(node) is dict]  # [node._properties.get('name') for node in results.graph().nodes]
\ No newline at end of file
diff --git a/sg-backend/main.py b/sg-backend/main.py
index caa72e7..0964d40 100644
--- a/sg-backend/main.py
+++ b/sg-backend/main.py
@@ -1,7 +1,33 @@
+import json
+import os
+
 from fastapi import FastAPI
+from pydantic import BaseModel
+from starlette.middleware.cors import CORSMiddleware
+
+from db import Neo4j
+
+
+class SoftwareSearch(BaseModel):
+    software1: str
+    software2: str
 
+
+driver = Neo4j(os.environ.get("NEO4J_URI"), os.environ.get("NEO4J_USER"), os.environ.get("NEO4J_PASSWORD"))
 app = FastAPI()
 
+origins = [
+    os.getenv("FRONTEND_URL"),
+]
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=origins,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
 
 @app.get("/")
 async def root():
@@ -10,4 +36,23 @@ async def root():
 
 @app.get("/hello/{name}")
 async def say_hello(name: str):
-    return {"message": f"Hello {name}"}
\ No newline at end of file
+    return {"message": f"Hello {name}"}
+
+
+@app.get("/graph")
+async def graph():
+    results = driver.get_graph_data()
+    return json.loads(results[0]['data'])
+
+
+@app.get("/software")
+async def software():
+    results = driver.get_softwares()
+    return {"softwares": json.loads(json.dumps(results))}
+
+
+@app.post("/software-search")
+async def software_search(search: SoftwareSearch):
+    print(search.software1, search.software2)
+    res = driver.get_softwares_shortest_path(search)
+    return {"softwares": res}
\ No newline at end of file
diff --git a/sg-backend/poetry.lock b/sg-backend/poetry.lock
index 89de34e..85f00f4 100644
--- a/sg-backend/poetry.lock
+++ b/sg-backend/poetry.lock
@@ -68,6 +68,17 @@ category = "main"
 optional = false
 python-versions = ">=3.5"
 
+[[package]]
+name = "neo4j"
+version = "5.0.1"
+description = "Neo4j Bolt driver for Python"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+pytz = "*"
+
 [[package]]
 name = "pydantic"
 version = "1.10.2"
@@ -83,6 +94,14 @@ typing-extensions = ">=4.1.0"
 dotenv = ["python-dotenv (>=0.10.4)"]
 email = ["email-validator (>=1.0.3)"]
 
+[[package]]
+name = "pytz"
+version = "2022.2.1"
+description = "World timezone definitions, modern and historical"
+category = "main"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "sniffio"
 version = "1.3.0"
@@ -131,7 +150,7 @@ standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)",
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.10"
-content-hash = "788d09d1be0c296c1d42382d6b13643f667a655479b71b3fb4c34cb721056840"
+content-hash = "92d7313563fc036bb36381d94d139862865b3a018e3849a340b8bc16513df739"
 
 [metadata.files]
 anyio = [
@@ -158,6 +177,9 @@ idna = [
     {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
     {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
 ]
+neo4j = [
+    {file = "neo4j-5.0.1.tar.gz", hash = "sha256:2330d1b8295b6afb39f23a001f5b0aecae6ca5895cc5b7af3413e326bbd1979c"},
+]
 pydantic = [
     {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
     {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
@@ -196,6 +218,10 @@ pydantic = [
     {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
     {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
 ]
+pytz = [
+    {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"},
+    {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"},
+]
 sniffio = [
     {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
     {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
diff --git a/sg-backend/pyproject.toml b/sg-backend/pyproject.toml
index 6e872c7..b387455 100644
--- a/sg-backend/pyproject.toml
+++ b/sg-backend/pyproject.toml
@@ -4,14 +4,15 @@ version = "0.1.0"
 description = ""
 authors = ["Guillaume Schurck <g.schurck@gmail.com>"]
 readme = "README.md"
-packages = [{include = "sg_backend"}]
+packages = []
 
 [tool.poetry.dependencies]
 python = "^3.10"
 fastapi = "^0.85.0"
 uvicorn = "^0.18.3"
+neo4j = "^5.0.1"
 
 
 [build-system]
 requires = ["poetry-core"]
-build-backend = "poetry.core.masonry.api"
+build-backend = "poetry.core.masonry.api"
\ No newline at end of file
diff --git a/sg-backend/test_main.http b/sg-backend/test_main.http
index c7ccc0f..7a5db99 100644
--- a/sg-backend/test_main.http
+++ b/sg-backend/test_main.http
@@ -2,8 +2,13 @@
 
 GET http://127.0.0.1:8000/
 Accept: application/json
+
 ###
 
 GET http://127.0.0.1:8000/hello/User
 Accept: application/json
-###
\ No newline at end of file
+
+###
+
+GET http://127.0.0.1:8000/graph
+Accept: application/json
\ No newline at end of file
-- 
GitLab