-
Notifications
You must be signed in to change notification settings - Fork 130
/
cli.py
250 lines (228 loc) · 8.29 KB
/
cli.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from hydrus.app_factory import app_factory
from hydrus.extensions.socketio_factory import create_socket
from hydrus.utils import (
set_session,
set_doc,
set_hydrus_server_url,
set_token,
set_api_name,
set_authentication,
set_page_size,
set_pagination,
)
from hydrus.data import doc_parse
from hydra_python_core import doc_maker
from hydrus.data.db_models import Base, create_database_tables
from hydrus.data.user import add_user
from hydrus.data.exceptions import UserExists
from hydrus.extensions.stale_records_cleanup import remove_stale_modification_records
from hydra_openapi_parser.openapi_parser import parse
from importlib.machinery import SourceFileLoader
import json
import click
import yaml
from hydrus.conf import APIDOC_OBJ, FOUND_DOC
@click.group()
def startserver():
"""
Python Hydrus CLI.
"""
pass
@startserver.command()
@click.option(
"--adduser",
"-u",
default=tuple([1, "test"]),
help="Adds a new user to the API.",
nargs=2,
type=(int, str),
)
@click.option("--api", "-a", default="serverapi", help="The API name.", type=str)
@click.option(
"--auth/--no-auth", default=True, help="Set authentication to True or False."
)
@click.option(
"--dburl", default="sqlite:///database.db", help="Set database url.", type=str
)
@click.option(
"--hydradoc",
"-d",
default=None,
help="Location to HydraDocumentation (JSON-LD) of server.",
type=str,
)
@click.option(
"--port", "-p", default=8080, help="The port the app is hosted at.", type=int
)
@click.option(
"--pagesize", "-ps", default=10, help="Maximum size of a page(view)", type=int
)
@click.option(
"--pagination/--no-pagination", default=True, help="Enable or disable pagination."
)
@click.option(
"--token/--no-token", default=True, help="Toggle token based user authentication."
)
@click.option(
"--serverurl", default="http://localhost", help="Set server url", type=str
)
@click.option(
"--use-db/--no-use-db", default=False, help="Use previously existing database"
)
@click.option(
"--stale_records_removal_interval",
default=900,
help="Interval period between removal of stale modification records.",
type=int,
)
def serve(
adduser: tuple,
api: str,
auth: bool,
dburl: str,
pagination: bool,
hydradoc: str,
port: int,
pagesize: int,
serverurl: str,
token: bool,
use_db: bool,
stale_records_removal_interval: int,
) -> None:
"""
Starts up the server.
\f
:param adduser <tuple> : Contains the user credentials.
:param api <str> : Sets the API name for the server.
:param auth <bool> : Toggles the authentication.
:param dburl <str> : Sets the database URL.
:param pagination <bool>: Toggles the pagination.
:param hydradoc <str> : Sets the link to the HydraDoc file
(Supported formats - [.py, .jsonld, .yaml])
:param port <int> : Sets the API server port.
:param pagesize <int> : Sets maximum size of page(view).
:param serverurl <str> : Sets the API server url.
:param token <bool> : Toggle token based user auth.
:stable_records_removal_interval <int> : Interval period between removal
of stale modification records.
:return : None
Raises:
Error: If `hydradoc` is not of a supported format[.py, .jsonld, .yaml].
"""
# The database connection URL
# See http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html for more info
# DB_URL = 'sqlite:///database.db'
DB_URL = dburl
# Define the server URL, this is what will be displayed on the Doc
HYDRUS_SERVER_URL = f"{serverurl}:{str(port)}/"
# The name of the API or the EntryPoint, the api will be at
# http://localhost/<API_NAME>
API_NAME = api
click.echo("Setting up the database")
# Create a connection to the database you want to use
engine = create_engine(DB_URL, connect_args={"check_same_thread": False})
# Define the Hydra API Documentation
# NOTE: You can use your own API Documentation and create a HydraDoc object
# using doc_maker or you may create your own HydraDoc Documentation using
# doc_writer [see hydra_python_core/doc_writer_sample]
click.echo("Creating the API Documentation")
if hydradoc:
# Getting hydradoc format
# Currently supported formats [.jsonld, .py, .yaml]
try:
hydradoc_format = hydradoc.split(".")[-1]
if hydradoc_format == "jsonld":
with open(hydradoc, "r") as f:
doc = json.load(f)
elif hydradoc_format == "py":
doc = SourceFileLoader("doc", hydradoc).load_module().doc
elif hydradoc_format == "yaml":
with open(hydradoc, "r") as stream:
doc = parse(yaml.load(stream))
else:
raise ("Error - hydradoc format not supported.")
click.echo(f"Using {hydradoc} as hydradoc")
apidoc = doc_maker.create_doc(doc, HYDRUS_SERVER_URL, API_NAME)
except BaseException:
if FOUND_DOC:
click.echo(
"Problem parsing specified hydradoc file"
"Using hydradoc from environment variable"
)
else:
click.echo(
"Problem parsing specified hydradoc file, "
"using sample hydradoc as default."
)
apidoc = doc_maker.create_doc(APIDOC_OBJ, HYDRUS_SERVER_URL, API_NAME)
else:
if FOUND_DOC:
click.echo(
"No hydradoc specified, using hydradoc from environment variable."
)
else:
click.echo(
"No hydradoc specified, using sample hydradoc as default.\n"
"For creating api documentation see this "
"https://www.hydraecosystem.org/01-Usage.html#newdoc\n"
"You can find the example used in hydrus/samples/hydra_doc_sample.py"
)
apidoc = doc_maker.create_doc(APIDOC_OBJ, HYDRUS_SERVER_URL, API_NAME)
# Start a session with the DB and create all classes needed by the APIDoc
session = scoped_session(sessionmaker(bind=engine))
# Get all the classes from the doc
# You can also pass dictionary defined in
# hydra_python_core/doc_writer_sample_output.py
classes = doc_parse.get_classes(apidoc)
# Insert them into the database
if use_db is False:
Base.metadata.drop_all(engine)
click.echo("Adding Classes and Properties")
create_database_tables(classes)
click.echo("Creating models")
Base.metadata.create_all(engine)
# Add authorized users and pass if they already exist
click.echo("Adding authorized users")
try:
add_user(id_=adduser[0], paraphrase=adduser[1], session=session)
except UserExists:
pass
# Insert them into the database
click.echo("Creating the application")
# Create a Hydrus app with the API name you want, default will be "api"
app = app_factory(API_NAME, apidoc.doc_name)
# Set the name of the API
# Create a socket for the app
socketio = create_socket(app, session)
click.echo("Starting the application")
#
# Nested context managers
#
# Use authentication for all requests
# Set the API Documentation
# Set HYDRUS_SERVER_URL
# Set the Database session
# Enable/disable pagination
# Set page size of a collection view
with set_authentication(app, auth) as _, set_token(app, token) as _, set_api_name(
app, api
) as _, set_doc(app, apidoc) as _, set_hydrus_server_url(
app, HYDRUS_SERVER_URL
) as _, set_session(
app, session
) as _, set_pagination(
app, pagination
) as _, set_page_size(
app, pagesize
) as _:
# Run a thread to remove stale modification records at some
# interval of time.
remove_stale_modification_records(session, stale_records_removal_interval)
# Start the hydrus app
socketio.run(app, port=port)
click.echo("Server running at:")
click.echo(f"{HYDRUS_SERVER_URL}{API_NAME}")
if __name__ == "__main__":
startserver()