Skip to content

Commit

Permalink
Adding a graphQL core extension
Browse files Browse the repository at this point in the history
  • Loading branch information
k9ert committed Feb 11, 2023
1 parent 816726b commit eb1800e
Show file tree
Hide file tree
Showing 22 changed files with 560 additions and 4 deletions.
4 changes: 4 additions & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ simple-websocket==0.8.1
protobuf==3.20.2
PyJWT==2.4.0
pytimeparse==1.1.8
# core-extension dependencies
cryptoadvance.spectrum==0.3.1
psycopg2-binary==2.9.5
typing_extensions==4.0.0
strawberry-graphql==0.155.2
# Extensions
cryptoadvance-liquidissuer==0.2.4
specterext-exfund==0.1.7
Expand Down
1 change: 1 addition & 0 deletions src/cryptoadvance/specter/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class BaseConfig(object):
"cryptoadvance.specterext.electrum.service",
"cryptoadvance.specterext.spectrum.service",
"cryptoadvance.specterext.stacktrack.service",
"cryptoadvance.specterext.graphql.service",
]

# This is just a placeholder in order to be aware that you cannot set this
Expand Down
8 changes: 5 additions & 3 deletions src/cryptoadvance/specter/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from cryptoadvance.specter.addresslist import Address
from cryptoadvance.specter.services import callbacks
from ..specter_error import SpecterInternalException


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -69,9 +70,10 @@ def _storage_manager(cls):
)

def callback(self, callback_id, *argv, **kwargv):
if callback_id == callbacks.after_serverpy_init_app:
if hasattr(self, "callback_after_serverpy_init_app"):
self.callback_after_serverpy_init_app(kwargv["scheduler"])
"""DEPRECATED"""
raise SpecterInternalException(
"This style of callback is deprecated. Please revisit the extension which is using it."
)

@classmethod
def set_current_user_service_data(cls, service_data: dict):
Expand Down
5 changes: 5 additions & 0 deletions src/cryptoadvance/specterext/devhelp/callbacks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from cryptoadvance.specter.services.callbacks import Callback


class my_callback(Callback):
id = "my_callback"
53 changes: 53 additions & 0 deletions src/cryptoadvance/specterext/devhelp/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[
{
"readlater": "no",
"annotations": [],
"tags": "no_tag",
"comments": [],
"shared": "yes",
"url": "http://a16z.com/2014/05/30/selling-saas-products-dont-sell-themselves/",
"desc": "\"Some people think the sales force’s job is to communicate value to customers.They’re wrong.\" @mdcranney : http://t.co/t146HVhuuT via @a16z",
"title": "If SaaS Products Sell Themselves, Why Do We Need Sales? | Andreessen Horowitz"
},
{
"readlater": "no",
"annotations": [],
"tags": "bitcoin,video,education",
"comments": [],
"shared": "yes",
"url": "https://www.youtube.com/watch?v=MhmxpfA7_Ag",
"desc": "",
"title": "Bitcoin Decrypted Part III: Social theory aspects - YouTube"
},
{
"readlater": "no",
"annotations": [],
"tags": "bitcoin,video,education",
"comments": [],
"shared": "yes",
"url": "https://www.youtube.com/watch?v=HvfO5u4ImAU",
"desc": "",
"title": "Bitcoin Decrypted Part II: Technical aspects (updated) - YouTube"
},
{
"readlater": "no",
"annotations": [],
"tags": "bitcoin,video,education",
"comments": [],
"shared": "yes",
"url": "https://www.youtube.com/watch?v=O4yrvRxFEWs",
"desc": "",
"title": "Bitcoin Decrypted Part I: Context and overview - YouTube"
},
{
"readlater": "no",
"annotations": [],
"tags": "no_tag",
"comments": [],
"shared": "yes",
"url": "http://t.co/Ub9XKrbInU",
"desc": "“You’ve been around long enough to know there won’t be any calm periods” @tomheon: Speeding Up Your Engineering Org http://t.co/Ub9XKrbInU",
"title": "“You’ve been around long enoug"
}
]

40 changes: 40 additions & 0 deletions src/cryptoadvance/specterext/devhelp/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import typing
import strawberry
from typing import List
import json
import os
import logging

logger = logging.getLogger(__name__)


@strawberry.type
class Bookmark:
url: str
desc: str
readlater: str
annotations: List[str]
tags: str
comments: List[str]
shared: str
title: str


def get_bookmarks() -> List[Bookmark]:
logger.info("CALLING get_bookmarks!!")
with open("src/cryptoadvance/specterext/devhelp/data.json") as json_file:
data = json.load(json_file)
listOfBookmarks = []
for item in data:
b = Bookmark(
url=item["url"],
desc=item["desc"],
readlater=item["readlater"],
annotations=[], # fixme
tags=item["tags"],
comments=item["comments"],
shared=item["shared"],
title=item["title"],
)
listOfBookmarks.append(b)
return listOfBookmarks
32 changes: 31 additions & 1 deletion src/cryptoadvance/specterext/devhelp/service.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import logging
import typing
from typing import List

import flask
import flask_login
import strawberry
from flask import current_app as app
from flask import render_template
from flask_login import current_user

from cryptoadvance.specter import util
from cryptoadvance.specter.services.service import Service, devstatus_alpha
from cryptoadvance.specter.specter_error import SpecterError
from cryptoadvance.specter.wallet import Wallet
from cryptoadvance.specter import util
from . import schema as devhelp_schema
from .callbacks import my_callback
from .console import Console
from .schema import get_bookmarks
import flask
import flask_login
from flask_login import current_user

logger = logging.getLogger(__name__)

Expand All @@ -22,6 +33,8 @@ class DevhelpService(Service):
has_blueprint = True
blueprint_module = "cryptoadvance.specterext.devhelp.controller"
devices = ["cryptoadvance.specterext.devhelp.devices.devhelpdevice"]
# Specifying the my_callback here:
callbacks = ["cryptoadvance.specterext.devhelp.callbacks"]
devstatus = devstatus_alpha
console = Console()
console.updateNamespace(
Expand All @@ -33,6 +46,23 @@ class DevhelpService(Service):
# ServiceEncryptedStorage field names for Swan
SPECTER_WALLET_ALIAS = "wallet"

def inform_world(self, msg="Hello World"):
"""Just a test method which you might want to call via the devhelper-console"""
# Calling the self-specified callback!
app.specter.service_manager.execute_ext_callbacks(my_callback, msg)

def callback_my_callback(self, msg):
"""Implementing the self declared callback-method
This is usually implemented in other extensions which depend on this one
"""
print(msg)

def callback_create_graphql_schema(self, field_list):
"""demoing the graphQL extension"""
# Add your fields to the Schema like this::
field_list.append(strawberry.field(name="bookmarks", resolver=get_bookmarks))
return field_list

@classmethod
def get_associated_wallet(cls) -> Wallet:
"""Get the Specter `Wallet` that is currently associated with this service"""
Expand Down
9 changes: 9 additions & 0 deletions src/cryptoadvance/specterext/graphql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Inspired by strawberry and the small [POC](https://github.com/k9ert/nomstr)

# potential issues

There might be a dependency conflict:
```
typing_extensions<5.0.0,>=3.7.4 (from strawberry-graphql==0.155.2->-r requirements.in (line 35))
typing-extensions<4.0,>=3.7 (from hwi==2.1.1->-r requirements.in (line 11))
```
Empty file.
15 changes: 15 additions & 0 deletions src/cryptoadvance/specterext/graphql/callbacks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from cryptoadvance.specter.services.callbacks import Callback


class create_graphql_schema(Callback):
"""
Gives you the ability to add to the GraphQL schema
example:
def callback_create_graphql_schema(self, field_list):
# Add your fields to the Schema like this::
field_list.append(strawberry.field(name="bookmarks", resolver=get_bookmarks))
return field_list
"""

id = "create_graphql_schema"
return_style = "middleware"
31 changes: 31 additions & 0 deletions src/cryptoadvance/specterext/graphql/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Here Configuration of your Extension takes place
"""

import os


def _get_bool_env_var(varname, default=None):

value = os.environ.get(varname, default)

if value is None:
return False
elif isinstance(value, str) and value.lower() == "false":
return False
elif bool(value) is False:
return False
else:
return bool(value)


class BaseConfig:
"""This is a extension-based Config which is used as Base"""

SPECTER_GRAPHQL_ACTIVE = _get_bool_env_var("SPECTER_GRAPHQL_ACTIVE", "True")


class ProductionConfig(BaseConfig):
"""This is a extension-based Config for Production"""

SPECTER_GRAPHQL_ACTIVE = _get_bool_env_var("SPECTER_GRAPHQL_ACTIVE", "False")
73 changes: 73 additions & 0 deletions src/cryptoadvance/specterext/graphql/controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import logging
from flask import redirect, render_template, request, url_for, flash
from flask import current_app as app
from flask_login import login_required, current_user
from strawberry.flask.views import GraphQLView

from cryptoadvance.specter.specter import Specter
from cryptoadvance.specter.services.controller import user_secret_decrypted_required
from cryptoadvance.specter.user import User
from cryptoadvance.specter.wallet import Wallet
from .service import GraphqlService


logger = logging.getLogger(__name__)

graphql_endpoint = GraphqlService.blueprint


def ext() -> GraphqlService:
"""convenience for getting the extension-object"""
return app.specter.ext["graphql"]


def specter() -> Specter:
"""convenience for getting the specter-object"""
return app.specter


# This endpoint is added dynamically in service.py
# @graphql_endpoint.route("/graphql", methods=["GET", "POST"])
# @app.csrf.exempt
# def index():
# view_func = GraphQLView.as_view("graphql_view", schema=schema2)
# return view_func()


@graphql_endpoint.route("/")
@login_required
def index():
return render_template(
"graphql/index.jinja",
)


@graphql_endpoint.route("/transactions")
@login_required
def schema():
# The wallet currently configured for ongoing autowithdrawals

return render_template(
"graphql/schema.jinja",
)


@graphql_endpoint.route("/settings", methods=["GET"])
@login_required
def settings_get():

return render_template(
"graphql/settings.jinja",
)


@graphql_endpoint.route("/settings", methods=["POST"])
@login_required
def settings_post():
show_menu = request.form["show_menu"]
user = app.specter.user_manager.get_user()
if show_menu == "yes":
user.add_service(GraphqlService.id)
else:
user.remove_service(GraphqlService.id)
return redirect(url_for(f"{ GraphqlService.get_blueprint_name()}.settings_get"))
30 changes: 30 additions & 0 deletions src/cryptoadvance/specterext/graphql/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from flask import current_app as app
from cryptoadvance.specter.managers.user_manager import UserManager

import typing
import strawberry
from typing import List
import json
import os
import logging

logger = logging.getLogger(__name__)


@strawberry.type
class User:
username: str


def get_users() -> List[User]:
um: UserManager = app.specter.user_manager
um.users
listOfUsers = []
for item in um.users:
u = User(username=item.username)
listOfUsers.append(u)
return listOfUsers


def create_fields():
return [strawberry.field(name="users", resolver=get_users)]
Loading

0 comments on commit eb1800e

Please sign in to comment.