Skip to content

Commit

Permalink
Enhanced the REST API by adding the ability to PATCH workspaces and m…
Browse files Browse the repository at this point in the history
…odules.
  • Loading branch information
lanmaster53 committed Oct 23, 2019
1 parent 59b604c commit 8f1f1f2
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 27 deletions.
21 changes: 16 additions & 5 deletions recon/core/web/static/recon.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,28 @@ $("#workspace").change(function(){
$("#exports").hide();
$("#data").hide();
var workspace = $("#workspace option:selected").text();
// set the workspace and create the summary
$.get("/api/workspaces/"+workspace, function(data, status){
// set the workspace
$.ajax({
type: "PATCH",
url: "/api/workspaces/"+workspace,
data: JSON.stringify({"status": "active"}),
processData: false,
contentType: 'application/json',
})
.done(function(data, status){
console.log("Workspace activated: "+workspace);
});
// create the summary
$.get("/api/dashboard", function(data, status){
createSummary(data);
});
// create the workspace
$.get("/api/tables/", function(data, status){
createWorkspace(data);
createWorkspace(data.tables);
});
// create the reports options
$.get("/api/reports/", function(data, status){
createReports(data);
createReports(data.reports);
});
// show the new elements on screen
$("#reports").show();
Expand Down Expand Up @@ -55,7 +66,7 @@ $("#tables-list").on("click", "li", function() {
});
$.get("/api/exports", function(data, status) {
// create the export menu
createExportMenu(data);
createExportMenu(data.exports);
});
// show the new elements on screen
$("#summary").hide();
Expand Down
132 changes: 110 additions & 22 deletions recon/core/web/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,112 @@ def index():
class ModuleList(Resource):

def get(self):
return list(recon._loaded_modules.keys())
return {
'modules': sorted(list(recon._loaded_modules.keys())),
}

api.add_resource(ModuleList, '/modules/')


class ModuleInst(Resource):

def get(self, module):
'''Returns information about the provided module.'''
module = recon._loaded_modules.get(module)
if module is None:
abort(404)
return {k: v for k, v in module.meta.items()}
meta = {k: v for k, v in module.meta.items()}
# provide options with more context
options = module.options.serialize()
if options:
meta['options'] = options
return meta

def patch(self, module):
'''Updates the provided module. Options are the only modifiable
property of a module object.'''
module = recon._loaded_modules.get(module)
if module is None:
abort(404)
options = request.json.get('options')
# process options
if options:
for option in options:
name = option.get('name')
value = option.get('value')
if name and value and name in module.options:
module.options[name] = value
module._save_config(name)
return self.get(module._modulename)

api.add_resource(ModuleInst, '/modules/<path:module>')


class WorkspaceList(Resource):

def get(self):
return recon._get_workspaces()
return {
'workspaces': sorted(recon._get_workspaces()),
}

api.add_resource(WorkspaceList, '/workspaces/')


class WorkspaceInst(Resource):

def get(self, workspace):
'''Returns information about the provided workspace. Only returns
options for the active workspace.'''
if workspace not in recon._get_workspaces():
abort(404)
# initialize the workspace if not already
if current_app.config['WORKSPACE'] != workspace:
# put the recon object in the right workspace
recon._init_workspace(workspace)
# add the workspace name the to global object
current_app.config['WORKSPACE'] = workspace
current_app.logger.info(f"Workspace initialized: {workspace}")
status = 'inactive'
options = []
if workspace == current_app.config['WORKSPACE']:
status = 'active'
options = recon.options.serialize()
return {
'name': workspace,
'status': status,
'options': options,
}

def patch(self, workspace):
'''Updates the provided workspace. When activating a workspace,
deactivates the currently activated workspace. Options for inactive
workspaces cannot be modified.'''
if workspace not in recon._get_workspaces():
abort(404)
status = request.json.get('status')
options = request.json.get('options')
# process status
if status:
# ignore everything but a request to activate
if status == 'active':
# only continue if the workspace is not already active
if current_app.config['WORKSPACE'] != workspace:
# initialize the workspace
recon._init_workspace(workspace)
# add the workspace name the to global object
current_app.config['WORKSPACE'] = workspace
current_app.logger.info(f"Workspace initialized: {workspace}")
# process options
if options:
# only continue if the workspace is active
if current_app.config['WORKSPACE'] == workspace:
for option in options:
name = option.get('name')
value = option.get('value')
if name and value and name in recon.options:
recon.options[name] = value
recon._save_config(name)
return self.get(workspace)

api.add_resource(WorkspaceInst, '/workspaces/<string:workspace>')


class DashboardInst(Resource):

def get(self):
# build the activity object
dashboard = recon.query('SELECT * FROM dashboard', include_header=True)
columns = dashboard.pop(0)
Expand All @@ -64,35 +134,45 @@ def get(self, workspace):
for table in tables:
count = recon.query(f"SELECT COUNT(*) AS 'COUNT' FROM {table}")
records.append({'name': table, 'count':count[0][0]})
# sort both lists in descending order
records.sort(key=lambda r: r['count'], reverse=True)
activity.sort(key=lambda m: m['runs'], reverse=True)
return {'records': records, 'activity': activity}
return {
'workspace': current_app.config['WORKSPACE'],
'records': records,
'activity': activity,
}

api.add_resource(WorkspaceInst, '/workspaces/<string:workspace>')
api.add_resource(DashboardInst, '/dashboard')


class ReportsList(Resource):
class ReportList(Resource):

def get(self):
return list(REPORTS.keys())
return {
'reports': sorted(list(REPORTS.keys())),
}

api.add_resource(ReportsList, '/reports/')
api.add_resource(ReportList, '/reports/')


class ReportsInst(Resource):
class ReportInst(Resource):

def get(self, report):
if report not in REPORTS:
abort(404)
return REPORTS[report]()

api.add_resource(ReportsInst, '/reports/<string:report>')
api.add_resource(ReportInst, '/reports/<string:report>')


class TableList(Resource):

def get(self):
return recon.get_tables()
return {
'workspace': current_app.config['WORKSPACE'],
'tables': sorted(recon.get_tables()),
}

api.add_resource(TableList, '/tables/')

Expand All @@ -115,13 +195,21 @@ def get(self, table):
if _format and _format in EXPORTS:
# any required serialization is handled at the exporter level
return EXPORTS[_format](rows=rows)
return {'columns': columns, 'rows': rows}
return {
'workspace': current_app.config['WORKSPACE'],
'table': table,
'columns': columns,
'rows': rows,
}

api.add_resource(TableInst, '/tables/<string:table>')

class ExportsList(Resource):

class ExportList(Resource):

def get(self):
return list(EXPORTS.keys())
return {
'exports': sorted(list(EXPORTS.keys())),
}

api.add_resource(ExportsList, '/exports')
api.add_resource(ExportList, '/exports')

0 comments on commit 8f1f1f2

Please sign in to comment.