#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache 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
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
#


# ResilientDB GraphQL
ResilientDB GraphQL Server

# Get Prepared 

(If you're using this for the first time, the steps below would be useful)

Install **Ubuntu 20.04** on your local machine.

Once installed, go to File Explorer -> Linux -> Ubuntu 20.04.

Clone this repository.

Install python3 (version - 3.9+) and ensure pip is installed using the command.

    sudo apt-get install python3-pip

Also make sure to install the venv module which creates a virtual Python environment that helps encapsulate the project's dependencies and prevents possible conflicts with the global Python environment. The command is:

    sudo apt-get install -y python3.10-venv

Create a virtual environment:

    python3 -m venv venv

Start a virtual environment:

    source venv/bin/activate

Go back from the virtual environment when you no longer need it:

    deactivate

# Using the SDK 

To use the SDK, you need to start a KV service first, which you can refer to the [resilientdb](https://github.com/resilientdb/resilientdb) repository and the [blog](https://blog.resilientdb.com/2022/09/28/GettingStartedNexRes.html). 

Then you should start the crow http service, which may take a few minutes at the first time.

    bazel build service/http_server:crow_service_main

    bazel-bin/service/http_server/crow_service_main service/tools/config/interface/service.config service/http_server/server_config.config

After starting the crow service, you can optionally test it by using the **curl** command to send HTTP requests.

    curl -X POST -d '{"id":"key1","value":"value1"}' 127.0.0.1:18000/v1/transactions/commit

    curl 127.0.0.1:18000/v1/transactions/key1

The expected output of the two commands above are: `id: key1` and `{"id":"key1","value":"value1"}`, respectively.

Start your virtual environment.

    source venv/bin/activate

Run the requirements.txt command to install the SDK related dependencies.

    pip3 install -r requirements.txt

Try the test script.

    python3 test_sdk.py

You have successfully run the script if you see the output: `The retrieved txn is successfully validated`

# GraphQL

<!-- 7. (Temporary resolve) Copy app.py to the nexres_sdk folder to ensure execution -->

(Optional) If there is an error while running any command relating to NexRes, this command below may help:

    sudo apt-get install python3.10-distutils

Note: If there is an error with the pip version, use the command:

    curl -sS https://bootstrap.pypa.io/get-pip.py | python3.10

Open a venv in the Ubuntu server and run the command: 

    source venv/bin/activate

    sudo apt-get install cloud-init

Install the remaining dependencies in venv

    python3 -m pip install strawberry-graphql

    python3 -m pip install flask_cors

**BUG FIX**: in the case requirements.txt doesn't work, and the `cloud-init` version is not found, edit the file to be:
```C
aiohttp==3.8.3
aiohttp-cors==0.7.0
aiosignal==1.3.1
anyio==3.6.2
ariadne==0.17.0
async-timeout==4.0.2
attrs==22.2.0
base58==2.1.0
certifi==2022.12.7
cffi==1.15.1
charset-normalizer==2.1.1
click==8.1.3
commonmark==0.9.1
cryptoconditions==0.8.1
cryptography==3.4.7
Flask==2.2.2
Flask-Cors==3.0.10
frozenlist==1.3.3
graphql-core==3.2.3
h11==0.14.0
idna==3.4
importlib-metadata==5.2.0
itsdangerous==2.1.2
Jinja2==3.1.2
libcst==0.4.9
MarkupSafe==2.1.1
multidict==6.0.4
mypy-extensions==0.4.3
pyasn1==0.4.8
pycparser==2.21
Pygments==2.14.0
PyNaCl==1.4.0
pysha3==1.0.2
python-dateutil==2.8.2
python-multipart==0.0.5
python-rapidjson==1.8
PyYAML==6.0
requests==2.28.1
rich==13.0.1
six==1.16.0
sniffio==1.3.0
starlette==0.23.1
strawberry-graphql==0.152.0
typing-inspect==0.8.0
typing_extensions==4.4.0
urllib3==1.26.13
uvicorn==0.20.0
Werkzeug==2.2.2
yarl==1.8.2
zipp==3.11.0
```

- You may get an error that says `Running setup.py install for <dependency> did not run successfully.`
- First, try `pip3 install wheel` or `pip install wheel`
- Next, install all missing dependencies manually
    - **NOTE** as long as you still have dependencies to install, you will get an error message, but this is okay!
    - From the stack trace, identify the module, then use `pip install <dependency>`
    - keep generating errors by running `python3 app.py` every time, and resolve each missing dependency
- Ex:
```C
Traceback (most recent call last):
File "/home/user/repos/NexresGraphQL/app.py", line 1, in <module>
from resdb_driver import Resdb
File "/home/user/repos/NexresGraphQL/resdb_driver/__init__.py", line 1, in <module>
from .driver import Resdb
File "/home/user/repos/NexresGraphQL/resdb_driver/driver.py", line 4, in <module>
from .offchain import prepare_transaction, fulfill_transaction
File "/home/user/repos/NexresGraphQL/resdb_driver/offchain.py", line 8, in <module>
from .transaction import Input, Transaction, TransactionLink, _fulfillment_from_details
File "/home/user/repos/NexresGraphQL/resdb_driver/transaction.py", line 14, in <module>
import base58
ModuleNotFoundError: No module named 'base58'
```
- In the last line, you can run `pip install base58`
- Keep running `python3 app.py` until you get to the broken dependency (ex: pysha3, sha3), then try running `sudo apt-get install python3.10-dev`
- Hopefully, all your dependencies should be installed, and running `python3 app.py` will run the server!

Start the app using:

    python3 app.py

This would be hosted on your local server to test the React App/Frontend.
Also, this would give info on the console about the POST command, which are used to create an account. This consists of 3 stages: publicKey. AES encrypted private key and decrypted privateKey

Note: To run Ubuntu virtual instance, open Command Prompt, then type `ubuntu2004` to open an instance

# Serve GraphQL Using Nginx

Install the Nginx web server package:

    sudo apt-get install nginx -y

Once the Nginx is installed, start and enable the Nginx service using the following command:

    systemctl start nginx
    systemctl enable nginx

Activate your virtual environment and install Gunicorn and Flask:

    source venv/bin/activate
    pip install wheel
    pip install gunicorn flask

Verify whether Gunicorn can serve the application correctly using the command below:

    gunicorn --bind 0.0.0.0:5000 wsgi:app

If everything is fine, you should get the following output:

    [2023-10-06 22:28:11 +0000] [24074] [INFO] Starting gunicorn 21.2.0
    [2023-10-06 22:28:11 +0000] [24074] [INFO] Listening at: http://0.0.0.0:5000 (24074)
    [2023-10-06 22:28:11 +0000] [24074] [INFO] Using worker: sync
    [2023-10-06 22:28:11 +0000] [24075] [INFO] Booting worker with pid: 24075

Press CTRL+C to stop the application. And deactivate the virtual environment.

    deactivate

Next, you will need to create a systemd unit file for the Flask application:

    sudo vim /etc/systemd/system/flask.service

Add the following lines:

    [Unit]
    Description=Gunicorn instance to serve Flask
    After=network.target
    [Service]
    User=root
    Group=www-data
    WorkingDirectory=\{Your Absolute Path to this Folder\}
    Environment="PATH=\{Your Absolute Path to this Folder\}/venv/bin"
    ExecStart=\{Your Absolute Path to this Folder\}/venv/bin/gunicorn --bind 0.0.0.0:8000 wsgi:app
    [Install]
    WantedBy=multi-user.target

Next, reload the systemd daemon with the following command:

    sudo systemctl daemon-reload

Next, start the flask service and enable it to start at system reboot:

    sudo systemctl start flask
    sudo systemctl enable flask

Next, verify the status of the flask with the following command:

    sudo systemctl status flask

Output:

    ● flask.service - Gunicorn instance to serve Flask
     Loaded: loaded (/etc/systemd/system/flask.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-10-06 22:20:00 UTC; 14min ago
    Main PID: 23745 (gunicorn)
      Tasks: 2 (limit: 38067)
     Memory: 40.6M
     CGroup: /system.slice/flask.service
             ├─23745 /home/ubuntu/ResilientDB-GraphQL/venv/bin/python3 /home/ubuntu/ResilientDB-GraphQL/venv/b>
             └─23747 /home/ubuntu/ResilientDB-GraphQL/venv/bin/python3 /home/ubuntu/ResilientDB-GraphQL/venv/b>


Next, you will need to configure Nginx as a reverse proxy to serve the Flask application through port 80. To do so, create an Nginx virtual host configuration file:

    sudo vim /etc/nginx/conf.d/flask.conf

Add the following lines:

    server {
        listen 80;
        server_name {The public IP address of your machine};
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

Save and close the file then verify the Nginx for any syntax error:

    sudo nginx -t

You should see the following output:

    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful

Finally, restart the Nginx service to apply the changes:

    sudo systemctl restart nginx

Then, test with:

    {The public IP address of your machine}/graphql
