Repository: apolloapi/modsysML (language: python)

Issue:
dev: initial push



Discussion / hints:

Fix patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..59f8870
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,17 @@
+---
+version: 2
+updates:
+  - package-ecosystem: github-actions
+    directory: /
+    schedule:
+      interval: daily
+
+  - package-ecosystem: pip
+    directory: /
+    schedule:
+      interval: weekly
+
+  - package-ecosystem: docker
+    directory: /
+    schedule:
+      interval: daily
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..e69de29
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
new file mode 100644
index 0000000..b96770f
--- /dev/null
+++ b/.github/workflows/pre-commit.yml
@@ -0,0 +1,21 @@
+---
+name: pre-commit
+
+on:
+  pull_request:
+  push:
+    branches: [main, develop]
+
+jobs:
+  pre-commit:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.10"
+      - uses: pre-commit/action@v3.0.0
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..62e7909
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,167 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+.vscode
+
+# django
+.env
+django_apolloapi/*
+django_apolloapi/
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+creds.json
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+#   This is especially recommended for binary packages to ensure reproducibility, and is more
+#   commonly ignored for libraries.
+#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+#   in version control.
+#   https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+#  and can be added to the global gitignore or merged into this file.  For a more nuclear
+#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..e7677e9
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "apollo/models/custom"]
+	path = apollo/models/custom
+	url = https://github.com/apolloapi/models
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..39e0243
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,24 @@
+default_language_version:
+  # default language version for each language
+  python: python3
+repos:
+  - repo: https://github.com/myint/autoflake
+    rev: v1.4
+    hooks:
+      - id: autoflake
+        args: [--in-place, --recursive]
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v3.4.0
+    hooks:
+      # See https://pre-commit.com/hooks.html for more hooks
+      - id: check-ast
+      - id: check-case-conflict
+      - id: check-executables-have-shebangs
+      - id: check-merge-conflict
+      - id: debug-statements
+      - id: end-of-file-fixer
+      - id: trailing-whitespace
+  - repo: https://github.com/psf/black
+    rev: 22.3.0
+    hooks:
+      - id: black
diff --git a/LICENSE b/LICENSE
index 85796ed..682d649 100644
--- a/LICENSE
+++ b/LICENSE
@@ -42,5 +42,3 @@ _your licenses_ are all the licenses granted to you for the software under these
 _use_ means anything you do with the software requiring one of your licenses.
 
 _trademark_ means trademarks, service marks, and similar rights.
-
-Copyright (c) 2023 Apollo API, all rights reserved.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5a34632
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+.DEFAULT_GOAL := build
+SHELL := /bin/bash
+# ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+APOLLOAPI := $(HOME)/apolloapi
+export APOLLOAPI
+
+# LINTS
+lint-commit-messages:
+	./scripts/lint-messages.sh
+
+
+test-lint:
+	pre-commit install
+	pre-commit run --all-files
+
+
+# BUILD
+build-run:
+	docker compose up -d
diff --git a/README.md b/README.md
index 62df702..fd5948c 100644
--- a/README.md
+++ b/README.md
@@ -29,15 +29,20 @@ Apollo gives you access to custom machine learning models, no-code platform and
 In your code you can write:
 
 ```ts
-Apollo.connect('postgres://username:password@hostname:port/database', ...) // Starts syncing content forever!
+Apollo.connect('postgres://username:password@hostnam...', ...) // Starts syncing content forever!
 
-Apollo.use('HiveAI', "bullying", ...) // Connect to existing providers!
+Apollo.use('OpenAI', "moderation", ...) // Connect to existing providers!
 
-Apollo.detectText('Phrase1', '>=', '0.8') // Create custom rules!
+Apollo.rule('Phrase1', '>=', '0.8') // Create custom rules!
 
-Apollo.use('Custom', "violence", ...) // Connect with our internal models!
+Apollo.use('Apollo', "violence", ...) // Connect with our internal models!
+
+// Detect bad actors at scale!
+Apollo.detectImage('Image1', 'contains', 'VERY_LIKELY') // Image Analysis/OCR
+Apollo.detectSpeech('Audio1', 'contains', 'UNLIKELY') // Audio Processing
+Apollo.detectVideo('Video1', 'contains', 'POSSIBLE') // Video Analysis
+Apollo.detectText('Phrase1', 'contains', 'UNKNOWN') // Text Analysis
 
-Apollo.detectImage('Image1', 'contains', 'VERY_LIKELY') // Detect bad actors at scale!
 ```
 
 Apollo then takes care of:
@@ -55,52 +60,100 @@ Apollo then takes care of:
 
 ## 🚀 Interesting, how can I try it?
 
-- Reach out to adrian@apolloapi.io
+Let's setup your first Integration in 2 minutes!
+
+It will pull from your local database (and keep it in sync).
+
+To start:
+
+```bash
+# install the cli-toolkit
+pip install apollo-sdk
+
+# enter a python repl or create a python file, up to you! (repl is easiest)
+python3
+```
+
+```python
+# import the package
+from apollo.client import Apollo
+
+# sync data from your database instance
+# (we support supabase at the current moment or postgresql via uri format)
+Apollo.connect("postgres://username:password@hostname:port/database_name")
+
+# If you want to test out operation on your external connection
+Apollo.fetch_tables()
+Apollo.query("desc", "table", "column")
+```
+
+Test sending your content to our API!
+
+...and create a workflow with a simple command:
+
+```python
+# import the package
+from apollo.client import Apollo
+
+# Use our custom model to test building decisions
+Apollo.use("Apollo")
 
-<!-- Let's setup your first Integration in 2 minutes!
+# We support video, speech, image and text. Try text!
+Apollo.detectText("Phrase1", "contains", "Threats")
+```
+
+That's all it takes!
+
+That's all it takes! You can check out [more on our notion page](https://cloudguruab.notion.site/Apollo-e5e347745c1e43798d849d79cce90aba).
+
+In practice, you probably want to use one of our native SDKs to interact with Apollo's API or use our custom browser client so you dont have to write code. If so, ping us at adrian@apolloapi.io!
 
-It will pull from your local database (and keep it in sync). -->
+## Contributing
 
-<!-- Test sending your content to our API! -->
+### 📦 pre-commit config
+
+As an open source project, Apollo welcomes contributions from the community at large. This isn’t an exhaustive reference and is a living document subject to change as needed when the project formalizes any practice or pattern.
 
-<!--
 Clone the repo and start Apollo locally...
 
 ```bash
 git clone https://github.com/apolloapi/apolloapi.git
-cd apolloapi && docker compose up
-``` -->
+cd apolloapi && python3 -m venv env && source env/bin/activate && pip install -r requirements.txt
+```
 
-<!-- ...and create a Integration with a simple CURL command: -->
+- After installing system dependencies be sure to install pre-commit for lint checks
 
-<!-- ```bash
-curl --request POST \
-    --url http://api.apolloapi.io/api/v1/content/ \
-    --header "Content-type: application/json" \
-    --data '{"content_id": "1234567", "user_id": "user123", "contenttype": "Post", "content": { "text": "some text posted on your platform or community"}}' 
-``` -->
+```bash
+pip install pre-commit
 
-<!-- That's all it takes! -->
+pre-commit install
 
-<!-- That's all it takes! You can check out [the list of all Pokémons in your local database](http://localhost:8080/?pgsql=nango-db&username=nango&db=nango&ns=public&select=_nango_raw) (password is `nango`). -->
+pre-commit run --all-files
+```
 
-In practice, you probably want to use one of our native SDKs to interact with Apollo's API or use our custom browser client so you dont have to write code.
+Apollo uses commit messages for automated generation of project changelog. For every pull request we request contributors to be compliant with the following commit message notation.
 
-<!-- ```js
-import { Nango } from "@nangohq/node-client";
-let config = {
-  response_path: "results", // The path to the Pokémons objects in the response.
-  paging_url_path: "next", // The path to the next page's url in the response.
-};
-await Nango.sync("https://pokeapi.co/api/v2/pokemon", config);
-``` -->
+```
+<type>: <summary>
+
+<body>
+```
+
+Accepted `<type>` values:
+new - newly implemented user-facing features
+chg - changes in existing user-facing features
+fix - user-facing bugfixes
+oth - other changes which users should know about
+dev - any developer-facing changes, regardless of new/chg/fix status
+
+#### Summary (The first line)
+
+The first line should not be longer than 75 characters, the second line is always blank and other lines should be wrapped at 80 characters.
 
 ## 🔍 Neat, I would like to learn more
 
 ⭐ Follow our development by starring us here on GitHub ⭐
 
-<!-- - Explore some [Real world examples](https://www.thebriefnewsletter.com) -->
-
 - Share feedback or ask questions on the [Discord community](https://discord.gg/ZUH7f7AzUY)
 - [Chat with a member of the team](https://apolloapi.io) 👋
 - Check our [blog on Trust & Safety](https://www.thebriefnewsletter.com)
diff --git a/apollo/__init__.py b/apollo/__init__.py
new file mode 100644
index 0000000..ac54b08
--- /dev/null
+++ b/apollo/__init__.py
@@ -0,0 +1,36 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from apollo.database.supabase.base import AbstractSupabaseClient
+from apollo.database.firebase.base import AbstractFirebaseClient
+from apollo.service.json.base import AbstractRestClient
+from .const import SUPABASE_CLIENT_CLASS, FIREBASE_CLIENT_CLASS, REST_CLIENT_CLASS
+
+from django.utils.module_loading import import_string
+
+
+def get_supabase_client(connection_string) -> AbstractSupabaseClient:
+    client = import_string(SUPABASE_CLIENT_CLASS)
+    return client(connection_string)
+
+
+def get_firebase_client(service_account_key) -> AbstractFirebaseClient:
+    client = import_string(FIREBASE_CLIENT_CLASS)
+    return client(service_account_key)
+
+
+def get_json_client() -> AbstractRestClient:
+    client = import_string(REST_CLIENT_CLASS)
+    return client
diff --git a/apollo/client.py b/apollo/client.py
new file mode 100644
index 0000000..e6dd974
--- /dev/null
+++ b/apollo/client.py
@@ -0,0 +1,118 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from apollo.resource import General
+from apollo.exceptions import ExecutionError, EmptyResultsWarning
+
+import itertools
+
+
+class Apollo(General):
+    @classmethod
+    def set_context(cls, query_type, table, col):
+        # oldest order
+        if query_type == "asc":
+            cls._context[
+                query_type
+            ] = f"SELECT * FROM {table} ORDER BY {col} ASC LIMIT 100;"
+
+        # newest
+        if query_type == "desc":
+            cls._context[
+                query_type
+            ] = f"SELECT * FROM {table} ORDER BY {col} DESC LIMIT 100;"
+
+        return cls._context[query_type]
+
+    # fetch all available tables
+    @classmethod
+    def fetch_tables(cls):
+        """
+        Fetch all the tables for your external resource using Apollo
+
+        Args:
+        None
+
+        Returns:
+        list: All the available tables for your resource
+
+        Next steps:
+        [Success]: Run a query with query
+        """
+        try:
+            response = cls.psql_curs.execute(cls._context["all_tables"])
+        except Exception as err:
+            raise ExecutionError(err)
+
+        return list(itertools.chain.from_iterable(response))
+
+    # # execute a sql query
+    @classmethod
+    def query(cls, query_type, table, col):
+        """
+        Query your external resource using Apollo
+
+        Args:
+        query_type (str): The sort order for your query
+        table (str): The table to query
+        col (str): The column to sort by
+
+        Returns:
+        dict: result of query
+        """
+        query_string = cls.set_context(query_type, table, col)
+
+        try:
+            response = cls.psql_curs.execute(query_string)
+        except Exception as err:
+            raise ExecutionError(err)
+
+        return cls._manager.convert_dict(response)
+
+    @classmethod
+    def connect(cls, db_url, *args, **kwargs):
+        """
+        Sync data with Apollo to begin building decision trees
+
+        Args:
+        db_url (str): The database URL to connect to
+
+        Returns:
+        str: A message indicating that the connection was successful
+
+        Next steps:
+        [Success]: Syncing data to Apollo, next steps below;
+            1. Apollo.fetch_tables()
+            2. Apollo.query([desc/asc], [table], [column])
+        """
+        cls.psql_curs = cls._manager.connect_to_prefix(db_url)
+        return "Syncing data with Apollo"
+
+    @classmethod
+    def use(cls, provider, *args, **kwargs):
+        if provider == "Apollo":
+            cls.model = "Apollo"
+            return f"Connected to {provider} provider, using Safety model"
+        else:
+            return f"Provider {provider} not found"
+
+    @classmethod
+    def detectText(cls, text, operator, threshold):
+        if cls.model:
+            print(cls.model)
+            conn = cls._service_manager.connect()
+            return conn.make_https_request({"rule": f"{text} {operator} {threshold}"})
+        else:
+            return "No provider connected"
diff --git a/apollo/const.py b/apollo/const.py
new file mode 100644
index 0000000..641f181
--- /dev/null
+++ b/apollo/const.py
@@ -0,0 +1,49 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from dotenv import load_dotenv
+
+import os
+
+load_dotenv()
+
+
+# Supabase support
+SUPABASE_CLIENT_CLASS = os.environ.get(
+    "SUPABASE_CLIENT_CLASS", "apollo.database.supabase.SupabaseClient"
+)
+
+# Firebase support
+FIREBASE_CLIENT_CLASS = os.environ.get(
+    "FIREBASE_CLIENT_CLASS", "apollo.database.firebase.FirebaseClient"
+)
+
+# Service client
+REST_CLIENT_CLASS = os.environ.get(
+    "REST_CLIENT_CLASS", "apollo.service.json.RestClient"
+)
+
+# Hard code sql queries to prevent injection
+QUERY_CONTEXT = {
+    "asc": "",
+    "desc": "",
+    "all_tables": "select table_name from information_schema.tables where table_schema='public'",
+}
+
+# Test account for firebase
+TEST_ACCOUNT = os.environ.get("test_account", None)
+
+# Service SDK test token, THIS IS PUBLIC
+test_token = os.environ.get("test_token", "81ecfd535e2d0c402bc84be64f31edcbed643d3e")
diff --git a/apollo/database/__init__.py b/apollo/database/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/database/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/database/firebase/__init__.py b/apollo/database/firebase/__init__.py
new file mode 100644
index 0000000..6222fd3
--- /dev/null
+++ b/apollo/database/firebase/__init__.py
@@ -0,0 +1,19 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from .cloud import FirebaseClient
+from .local import FakeFirebaseClient
+
+__all__ = ["FirebaseClient", "FakeFirebaseClient"]
diff --git a/apollo/database/firebase/base.py b/apollo/database/firebase/base.py
new file mode 100644
index 0000000..4b95bde
--- /dev/null
+++ b/apollo/database/firebase/base.py
@@ -0,0 +1,47 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+class AbstractFirebaseClient:
+    def execute(self, nosql):
+        return NotImplementedError
+
+    def init_with_service_account(self):
+        """
+        Initialize the Firestore DB client using a service account
+        :param file_path: path to service account
+        :return: firestore
+        """
+        return NotImplementedError
+
+    def init_with_project_id(self):
+        """
+        Initialize the Firestore DB client using a GCP project ID
+        :param project_id: The GCP project ID
+        :return: firestore
+        """
+        return NotImplementedError
+
+    def init_with_database_url(self):
+        return NotImplementedError
+
+    def close():
+        return NotImplementedError
+
+    def execute(self, collection_name):
+        return NotImplementedError
+
+    def list_tables(self):
+        return NotImplementedError
diff --git a/apollo/database/firebase/cloud.py b/apollo/database/firebase/cloud.py
new file mode 100644
index 0000000..dedaea5
--- /dev/null
+++ b/apollo/database/firebase/cloud.py
@@ -0,0 +1,103 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import firebase_admin
+from firebase_admin import credentials
+from firebase_admin import firestore
+
+from apollo.exceptions import FirebaseConnectionError
+from apollo.database.firebase.base import AbstractFirebaseClient
+from apollo.const import TEST_ACCOUNT
+
+
+# https://firebase.google.com/docs/admin/setup?authuser=0#python
+class FirebaseClient(AbstractFirebaseClient):
+    def __init__(self, service_account_key):
+        self.project_id = None
+        self.database_url = None
+        self.service_account_key = service_account_key
+
+    def init_with_service_account(self):
+        """
+        Initialize the Firestore DB client using a service account
+        :param file_path: path to service account
+        :return: firestore
+        """
+        # cred = credentials.Certificate(self.service_account_key) FIXME:
+        cred = credentials.Certificate(TEST_ACCOUNT)
+        try:
+            firebase_admin.initialize_app(cred)
+            return firestore.client()
+        except Exception as err:
+            raise FirebaseConnectionError(err)
+
+    def init_with_project_id(self):
+        """
+        Initialize the Firestore DB client using a GCP project ID
+        :param project_id: The GCP project ID
+        :return: firestore
+        """
+        # cred = credentials.ApplicationDefault()
+        app_options = {"projectId": self.project_id}
+        try:
+            firebase_admin.get_app()
+        except ValueError:
+            firebase_admin.initialize_app(options=app_options)
+        return firestore.client()
+
+    def init_with_database_url(self):
+        cred = credentials.Certificate(self.service_account_key)
+        try:
+            firebase_admin.initialize_app(cred, {"databaseURL": self.database_url})
+            return firestore.client()
+        except Exception as err:
+            raise FirebaseConnectionError(err)
+
+    def close(self):
+        # NOTE can check for live connections as well: firebase_admin._apps)
+        firebase_admin.delete_app(firebase_admin.get_app())
+
+    # TODO: add custom exception and return a custom warning
+    def execute(self, collection_name):
+        if not self.service_account_key:
+            return
+
+        try:
+            cursor = self.init_with_service_account()
+        except:
+            raise ValueError
+
+        response = [
+            item.to_dict()
+            for item in cursor.collection(collection_name).limit(10).get()
+        ]
+        self.close()
+
+        return response
+
+    # TODO: add custom exception and return a custom warning
+    def list_tables(self):
+        if not self.service_account_key:
+            return
+
+        try:
+            cursor = self.init_with_service_account()
+        except:
+            raise ValueError
+
+        collections = [collection.id for collection in cursor.collections()]
+        self.close()
+
+        return collections
diff --git a/apollo/database/firebase/local.py b/apollo/database/firebase/local.py
new file mode 100644
index 0000000..28538e7
--- /dev/null
+++ b/apollo/database/firebase/local.py
@@ -0,0 +1,50 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from apollo.database.firebase.base import AbstractFirebaseClient
+
+
+class FakeFirebaseClient(AbstractFirebaseClient):
+    def execute(self, nosql):
+        pass
+
+    def init_with_service_account(self):
+        """
+        Initialize the Firestore DB client using a service account
+        :param file_path: path to service account
+        :return: firestore
+        """
+
+    def init_with_project_id(self):
+        """
+        Initialize the Firestore DB client using a GCP project ID
+        :param project_id: The GCP project ID
+        :return: firestore
+        """
+
+    def init_with_database_url(self):
+        pass
+
+    def close():
+        pass
+
+    def execute(self, nosql):
+        pass
+
+    def execute(self, collection_name):
+        pass
+
+    def list_tables(self):
+        pass
diff --git a/apollo/database/mongo/__init__.py b/apollo/database/mongo/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/database/mongo/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/database/mysql/__init__.py b/apollo/database/mysql/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/database/mysql/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/database/postgres/__init__.py b/apollo/database/postgres/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/database/postgres/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/database/supabase/__init__.py b/apollo/database/supabase/__init__.py
new file mode 100644
index 0000000..ba3c2dc
--- /dev/null
+++ b/apollo/database/supabase/__init__.py
@@ -0,0 +1,19 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from .cloud import SupabaseClient
+from .local import FakeSupabaseClient
+
+__all__ = ["SupabaseClient", "FakeSupabaseClient"]
diff --git a/apollo/database/supabase/base.py b/apollo/database/supabase/base.py
new file mode 100644
index 0000000..48e8447
--- /dev/null
+++ b/apollo/database/supabase/base.py
@@ -0,0 +1,19 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+class AbstractSupabaseClient:
+    def execute(self, sql):
+        raise NotImplementedError
diff --git a/apollo/database/supabase/cloud.py b/apollo/database/supabase/cloud.py
new file mode 100644
index 0000000..7711cd4
--- /dev/null
+++ b/apollo/database/supabase/cloud.py
@@ -0,0 +1,46 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import psycopg as pgi_connector
+import psycopg2
+import psycopg2.extras
+
+from apollo.database.supabase.base import AbstractSupabaseClient
+from apollo.exceptions import PostgresConnectionError, PostgresExecutionError
+
+
+class SupabaseClient(AbstractSupabaseClient):
+    def __init__(self, connection_string: str):
+        self.secret = connection_string
+
+    def execute(self, sql):
+        if not self.secret:
+            raise PostgresExecutionError("empty secret")
+
+        try:
+            conn_dict = pgi_connector.conninfo.conninfo_to_dict(self.secret)
+            con = psycopg2.connect(**conn_dict)
+        except Exception as err:
+            raise PostgresConnectionError(err)
+
+        response = None
+        with con:
+
+            cursor = con.cursor(cursor_factory=psycopg2.extras.DictCursor)
+            cursor.execute(str(sql))
+
+            response = cursor.fetchall()
+
+        return response
diff --git a/apollo/database/supabase/local.py b/apollo/database/supabase/local.py
new file mode 100644
index 0000000..4f72b75
--- /dev/null
+++ b/apollo/database/supabase/local.py
@@ -0,0 +1,21 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from apollo.database.supabase.base import AbstractSupabaseClient
+
+
+class FakeSupabaseClient(AbstractSupabaseClient):
+    def execute(self, sql):
+        return sql
diff --git a/apollo/exceptions.py b/apollo/exceptions.py
new file mode 100644
index 0000000..afae432
--- /dev/null
+++ b/apollo/exceptions.py
@@ -0,0 +1,154 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+class SDKException(Exception):
+    """The base exception class for all exceptions this library raises."""
+
+    def __init__(self, message=None):
+        message = self.__class__.__name__ if message is None else message
+        super(Exception, self).__init__(message)
+
+
+class AuthorizationFailure(SDKException):
+    """Cannot authorize API client."""
+
+
+class EndpointException(SDKException):
+    """Something is rotten in Service Catalog."""
+
+
+class EndpointNotFound(EndpointException):
+    """Could not find requested endpoint in Service Catalog."""
+
+
+class EmptyCatalog(EndpointNotFound):
+    """The service catalog is empty."""
+
+
+class NoMatchingPlugin(SDKException):
+    """No matching plugins could be created with the provided parameters."""
+
+
+class InvalidResponse(SDKException):
+    """The response from the server is not valid for this request."""
+
+    def __init__(self, response):
+        super(InvalidResponse, self).__init__()
+        self.response = response
+
+
+class HttpException(SDKException):
+    def __init__(self, message, details=None):
+        super(HttpException, self).__init__(message)
+        self.details = details
+
+
+class MethodNotSupported(SDKException):
+    """The resource does not support this operation type."""
+
+
+class ResourceNotFound(SDKException):
+    """The requested resource was not found."""
+
+
+class DuplicateResource(SDKException):
+    """More than one resource exists with that name."""
+
+
+class FirebaseConnectionError(Exception):
+    """
+    There was an error connecting to the firebase client.
+    """
+
+    def __init__(self, obj, message="Unable to reach the firebase client"):
+        self.obj = obj
+        self.message = message
+        super().__init__(self.message)
+
+    def __str__(self):
+        return f"{self.obj} -> {self.message}"
+
+
+class PostgresConnectionError(Exception):
+    """
+    There was an error connecting to the postgres client.
+    """
+
+    def __init__(self, obj, message="Unable to reach the postgres client"):
+        self.obj = obj
+        self.message = message
+        super().__init__(self.message)
+
+    def __str__(self):
+        return f"{self.obj} [ERR] - {self.message}"
+
+
+class PostgresExecutionError(Exception):
+    """
+    There was an error connecting to the postgres client.
+    """
+
+    def __init__(self, obj, message="You're missing a connection string"):
+        self.obj = obj
+        self.message = message
+        super().__init__(self.message)
+
+    def __str__(self):
+        return f"{self.obj} [ERR] - {self.message}"
+
+
+class ClientSDKSetupError(Exception):
+    """
+    There was an error accepting your auth token.
+    """
+
+    def __init__(self, obj, message="Unable to init the headless sdk"):
+        self.obj = obj
+        self.message = message
+        super().__init__(self.message)
+
+    def __str__(self):
+        return f"{self.obj} -> {self.message}"
+
+
+class ExecutionError(Exception):
+    """
+    There was an error executing sql query.
+    """
+
+    def __init__(
+        self, obj, message="Unable to execute sql query, check your connection info"
+    ):
+        self.obj = obj
+        self.message = message
+        super().__init__(self.message)
+
+    def __str__(self):
+        return f"{self.obj} -> {self.message}"
+
+
+class EmptyResultsWarning(Exception):
+    """
+    There were no results found.
+    """
+
+    def __init__(self, obj, message="Empty query results detected"):
+        self.obj = obj
+        self.message = message
+        super().__init__(self.message)
+
+    def __str__(self):
+        return f"{self.obj} -> {self.message}"
diff --git a/apollo/manager.py b/apollo/manager.py
new file mode 100644
index 0000000..e5d8570
--- /dev/null
+++ b/apollo/manager.py
@@ -0,0 +1,47 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from apollo import get_supabase_client, get_firebase_client, get_json_client
+from apollo.exceptions import EmptyResultsWarning
+
+
+class PostgresConnectionManager:
+    @staticmethod
+    def connect_to_prefix(uri):
+        return get_supabase_client(uri)
+
+    @staticmethod
+    def convert_dict(items):
+        if not items:
+            raise EmptyResultsWarning(items)
+
+        vector = []
+
+        for i in items:
+            vector.append(dict(i))
+
+        return vector
+
+
+class FirebaseConnectionManager:
+    @staticmethod
+    def connect_to_prefix(service_account_key):
+        return get_firebase_client(service_account_key)
+
+
+class JSONConnectionManager:
+    @staticmethod
+    def connect():
+        return get_json_client()
diff --git a/apollo/models/__init__.py b/apollo/models/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/models/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/models/aws/__init__.py b/apollo/models/aws/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/models/aws/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/models/custom b/apollo/models/custom
new file mode 160000
index 0000000..d1620f1
--- /dev/null
+++ b/apollo/models/custom
@@ -0,0 +1,1 @@
+Subproject commit d1620f1d4f811aedab8372d1cc6820c8139ec038
diff --git a/apollo/models/google/__init__.py b/apollo/models/google/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/models/google/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/models/microsoft/__init__.py b/apollo/models/microsoft/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/models/microsoft/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/models/openai/__init__.py b/apollo/models/openai/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/models/openai/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/resource.py b/apollo/resource.py
new file mode 100644
index 0000000..f04cb28
--- /dev/null
+++ b/apollo/resource.py
@@ -0,0 +1,49 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from apollo.manager import (
+    PostgresConnectionManager,
+    FirebaseConnectionManager,
+    JSONConnectionManager,
+)
+from apollo.const import QUERY_CONTEXT
+
+
+class Postgres:
+
+    # Specific sql queries
+    _context = QUERY_CONTEXT
+
+    # Database utility class
+    _manager = PostgresConnectionManager()
+
+    # curosr instance
+    psql_curs = None
+
+
+class Firebase:
+    pass
+
+
+class JSON:
+
+    # Service utility class
+    _service_manager = JSONConnectionManager()
+
+
+class General(Postgres, JSON):
+
+    # Current LLM to be used
+    model = None
diff --git a/apollo/service/__init__.py b/apollo/service/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/service/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/service/graphql/__init__.py b/apollo/service/graphql/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/service/graphql/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/apollo/service/json/__init__.py b/apollo/service/json/__init__.py
new file mode 100644
index 0000000..abb143f
--- /dev/null
+++ b/apollo/service/json/__init__.py
@@ -0,0 +1,19 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from .cloud import RestClient
+from .local import FakeRestClient
+
+__all__ = ["RestClient", "FakeRestClient"]
diff --git a/apollo/service/json/base.py b/apollo/service/json/base.py
new file mode 100644
index 0000000..24552f5
--- /dev/null
+++ b/apollo/service/json/base.py
@@ -0,0 +1,22 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+class AbstractRestClient:
+    def make_http_request(self, body, header, endpoint):
+        return NotImplementedError
+
+    def make_https_request(self, body, header, endpoint):
+        return NotImplementedError
diff --git a/apollo/service/json/cloud.py b/apollo/service/json/cloud.py
new file mode 100644
index 0000000..b61c0fb
--- /dev/null
+++ b/apollo/service/json/cloud.py
@@ -0,0 +1,35 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import requests
+
+from apollo.service.json.base import AbstractRestClient
+from apollo.const import test_token
+
+
+class RestClient(AbstractRestClient):
+    @staticmethod
+    def make_http_request():
+        return None
+
+    @staticmethod
+    def make_https_request(body):
+        response = requests.post(
+            "https://api.apolloapi.io/api/v1/content/",
+            headers={"Authorization": f"Token {test_token}"},
+            json=body,
+            timeout=10,
+        )
+        return response.json()
diff --git a/apollo/service/json/local.py b/apollo/service/json/local.py
new file mode 100644
index 0000000..b379134
--- /dev/null
+++ b/apollo/service/json/local.py
@@ -0,0 +1,24 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from apollo.service.json.base import AbstractRestClient
+
+
+class FakeRestClient(AbstractRestClient):
+    def make_http_request(self, body, header, endpoint):
+        pass
+
+    def make_https_request(self, body, header, endpoint):
+        pass
diff --git a/docs/api/client-setup.md b/docs/api/client-setup.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/api/creating-rules.md b/docs/api/creating-rules.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/api/getting-started.md b/docs/api/getting-started.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/api/sending-content.md b/docs/api/sending-content.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/connectors/feature-list.md b/docs/connectors/feature-list.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/connectors/getting-started.md b/docs/connectors/getting-started.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/connectors/support.md b/docs/connectors/support.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/contributing/changelog.md b/docs/contributing/changelog.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/contributing/code-of-conduct.md b/docs/contributing/code-of-conduct.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/contributing/code-rules.md b/docs/contributing/code-rules.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/contributing/contributors-agreement.md b/docs/contributing/contributors-agreement.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/contributing/documentation.md b/docs/contributing/documentation.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/contributing/getting-started.md b/docs/contributing/getting-started.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/integrations/databases.md b/docs/integrations/databases.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/integrations/feature-list.md b/docs/integrations/feature-list.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/integrations/getting-started.md b/docs/integrations/getting-started.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/integrations/services.md b/docs/integrations/services.md
new file mode 100644
index 0000000..e69de29
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..62a87d2
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,67 @@
+asgiref==3.6.0
+attrs==22.1.0
+CacheControl==0.12.11
+cachetools==5.2.0
+certifi==2022.12.7
+cffi==1.15.1
+cfgv==3.3.1
+charset-normalizer==2.1.1
+click==8.1.3
+cryptography==40.0.1
+distlib==0.3.6
+Django==4.1.2
+exceptiongroup==1.0.4
+execnet==1.9.0
+filelock==3.8.0
+firebase-admin==6.1.0
+grpc-google-iam-v1==0.12.4
+grpcio==1.53.0
+grpcio-status==1.53.0
+gunicorn==20.1.0
+httplib2==0.21.0
+identify==2.5.22
+idna==3.4
+iniconfig==1.1.1
+libcst==0.4.9
+model-bakery==1.9.0
+msgpack==1.0.5
+mypy-extensions==0.4.3
+nodeenv==1.7.0
+oauth2client==4.1.3
+packaging==22.0
+platformdirs==3.2.0
+pluggy==1.0.0
+pre-commit==2.20.0
+proto-plus==1.22.2
+protobuf==4.22.3
+psycopg==3.1.6
+psycopg2==2.9.3
+psycopg2-binary==2.9.6
+py==1.11.0
+pyasn1==0.4.8
+pyasn1-modules==0.2.8
+pycparser==2.21
+PyJWT==2.6.0
+pyparsing==3.0.9
+pytest==7.2.0
+pytest-django==4.5.2
+pytest-env==0.8.1
+pytest-forked==1.4.0
+pytest-split==0.8.0
+pytest-xdist==2.5.0
+python-dotenv==1.0.0
+pytz==2022.6
+PyYAML==6.0
+requests==2.28.1
+rsa==4.9
+six==1.16.0
+sqlparse==0.4.3
+toml==0.10.2
+tomli==2.0.0
+typing-inspect==0.8.0
+typing_extensions==4.4.0
+tzdata==2022.7
+uritemplate==4.1.1
+urllib3==1.26.13
+virtualenv==20.21.0
+wait-for-it==2.2.1
diff --git a/scripts/lint-message.sh b/scripts/lint-message.sh
new file mode 100644
index 0000000..2b16e08
--- /dev/null
+++ b/scripts/lint-message.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+set -euo pipefail
+
+ALLOWED_TYPES=(
+    new
+    fix
+    chg
+    oth
+    dev
+)
+
+
+MAX_FIRST_LINE_LENGTH=75
+
+function lint_commit_message {
+    local commit_msg="$1"
+    local first_line;
+    first_line=$(head -n1 <<<"$commit_msg")
+
+    local first_line_length;
+    first_line_length=$(wc -c <<<"$first_line")
+    if [ "$first_line_length" -gt $MAX_FIRST_LINE_LENGTH ]; then
+        error_header_first_line "$first_line"
+        echo "The line has $first_line_length characters, but $MAX_FIRST_LINE_LENGTH is maximum (incl. newline)."
+        exit 1
+    fi
+
+    # local first_line_regex='^([^\(:]+): (.+)$'
+    # The following should work once we support scopes:
+    local first_line_regex='^([^\(:]+)(\([^)]+\))?!?: (.+)$'
+    if [[ "$first_line" =~ $first_line_regex ]]; then
+        local type="${BASH_REMATCH[1]}"
+        local scope_parens="${BASH_REMATCH[2]}"
+        local scope=${scope_parens/(/}
+        scope=${scope/)/}
+        if ! printf '%s\n' "${ALLOWED_TYPES[@]}" | grep -qxF "$type"; then
+            error_header_first_line "$first_line"
+            echo "Unrecognized type '$type'. Allowed types: ${ALLOWED_TYPES[*]}"
+            exit 1
+        fi
+        if [ -n "$scope_parens" ]; then
+            echo "Scopes are presently not allowed in commit messages."
+            exit 1
+        fi
+        # if [ -n "$scope_parens" ] && ! printf '%s\n' "${ALLOWED_SCOPES[@]}" | grep -qxF "$scope"; then
+        #     error_header_first_line "$first_line"
+        #     echo "Unrecognized scope '$scope'. Allowed scopes: ${ALLOWED_SCOPES[*]}"
+        #     exit 1
+        # fi
+    else
+        error_header_first_line "$first_line"
+        echo "The first line does not match pattern. Examples of first line formatting:"
+        echo "type: summary"
+        echo "type!: summary"
+        # echo "type(scope): summary"
+        # echo "type(scope)!: summary"
+        echo
+        echo "Allowed types: ${ALLOWED_TYPES[*]}"
+        # echo "Allowed scopes: ${ALLOWED_SCOPES[*]}"
+        echo "Variants with exclamation mark should be used for breaking changes."
+        exit 1
+    fi
+
+    local num_lines;
+    num_lines=$(wc -l <<<"$commit_msg")
+    if [ "$num_lines" -gt 1 ]; then
+        if [ "$num_lines" -lt 3 ]; then
+            error_header_first_line "$first_line"
+            echo "The message has $num_lines lines. It must have either 1, or 3+."
+            exit 1
+        fi
+        local second_line;
+        second_line=$(sed -n 2p <<<"$commit_msg")
+        if [ -n "$second_line" ]; then
+            error_header_first_line "$first_line"
+            echo "The 2nd line of commit message must be empty."
+            exit 1
+        fi
+        local third_line;
+        third_line=$(sed -n 3p <<<"$commit_msg")
+        if [ -z "$third_line" ]; then
+            error_header_first_line "$first_line"
+            echo "The 3rd line of commit message must not be empty."
+            exit 1
+        fi
+    fi
+}
+
+function error_header_first_line {
+    echo "ERROR: Commit does not follow convention:"
+    echo "$1"
+    echo
+}
+
+# change this back to original dir path before testing on line 98
+lint_commit_message "$(< /dev/stdin)"
diff --git a/scripts/lint-messages.sh b/scripts/lint-messages.sh
new file mode 100644
index 0000000..37f0b18
--- /dev/null
+++ b/scripts/lint-messages.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -euo pipefail
+
+DIR=$(dirname $(realpath $0))
+
+# The start of the range can be bumped up over time to something that
+# all live branches include. Currently pointing to initial commit.
+LINT_COMMITS_RANGE="9b161411b6cb68afa611a448436db93fbdc7f69c..HEAD"
+
+function lint_commits_in_range {
+    while read -r rev; do
+        lint_commit "$rev"
+    done < <(git rev-list --no-merges "$LINT_COMMITS_RANGE")
+}
+
+function lint_commit {
+    local rev="$1"
+    local commit_msg;
+    commit_msg=$(git log --format=%B -n 1 "$rev")
+    "$DIR"/lint-message.sh <<<"$commit_msg"
+}
+
+lint_commits_in_range "$LINT_COMMITS_RANGE"
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..baa8ed1
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from setuptools import setup
+
+setup(
+    name="apollo-sdk",
+    version="0.1.1",
+    description="Build automated decision making workflows. Aggregate your LLMs all into one place and build from there using one api.",
+    author="Apollo API, Inc.",
+    author_email="adrbrownx@gmail.com",
+    packages=["apollo"],
+    license="Elastic License v2",
+    install_requires=[
+        "Django==4.1.2",
+        "firebase-admin==6.1.0",
+        "grpcio==1.53.0",
+        "httplib2==0.21.0",
+        "protobuf==4.22.3",
+        "psycopg2-binary==2.9.6",
+        "python-dotenv==1.0.0",
+        "psycopg==3.1.6",
+        "requests==2.28.1",
+        "psycopg2==2.9.3",
+        "pytest==7.2.0",
+        "PyJWT==2.6.0",
+        "requests==2.28.1",
+        "six==1.16.0",
+        "sqlparse==0.4.3",
+        "urllib3==1.26.13",
+    ],
+    classifiers=[
+        "Intended Audience :: Developers",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+    ],
+)


Test patch:
diff --git a/apollo/tests/__init__.py b/apollo/tests/__init__.py
new file mode 100644
index 0000000..d1ce8af
--- /dev/null
+++ b/apollo/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 Adrian Brown
+# Copyright 2023 Apollo API, Inc.
+#
+#    Licensed under the Elastic License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         https://www.elastic.co/licensing/elastic-license
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..0e9d6a8
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,6 @@
+[pytest]
+DJANGO_SETTINGS_MODULE = apolloapi.settings.local
+
+addopts = --reuse-db --show-capture=no
+django_debug_mode = true
+python_files = tests.py test_*.py *_tests.py