Merge pull request #147 from hongwei1/master

feature/added api collection menu
This commit is contained in:
Simon Redfern 2021-10-19 10:38:27 +02:00 committed by GitHub
commit e38d6875fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 388 additions and 2 deletions

View File

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""
App config for config app
"""
from django.apps import AppConfig
class ApiCollectionsConfig(AppConfig):
"""Config for apicollections"""
name = 'apicollections'

View File

@ -0,0 +1,24 @@
from django import forms
class ApiCollectionsForm(forms.Form):
api_collections_body = forms.CharField(
label='API Collections Body',
widget=forms.Textarea(
attrs={
'class': 'form-control',
}
),
required=False
)
class ApiCollectionEndpointsForm(forms.Form):
operation_id = forms.CharField(
label='Operation Id',
widget=forms.TextInput(
attrs={
'class': 'form-control',
}
),
required=True
)

View File

@ -0,0 +1,34 @@
$(document).ready(function($) {
$('.runner button.forSave').click(function(e) {
e.preventDefault();
var t = $(this);
var runner = t.parent().parent().parent();
var api_collection_name = $(runner).find('.api_collection_name').val();
var api_collection_is_sharable = $(runner).find('.api_collection_is_sharable').val();
var api_collection_description = $(runner).find('.api_collection_description').val();
$('.runner button.forSave').attr("disabled","disabled");
$('.runner button.forDelete').attr("disabled","disabled");
$.post('save/apicollection', {
'api_collection_name': api_collection_name,
'api_collection_is_sharable': api_collection_is_sharable,
'api_collection_description': api_collection_description,
}, function (response) {
location.reload();
});
});
$('.runner button.forDelete').click(function(e) {
e.preventDefault();
var t = $(this);
var runner = t.parent().parent().parent();
var api_collection_id = $(runner).find('.api_collection_id').html();
$('.runner button.forSave').attr("disabled","disabled");
$('.runner button.forDelete').attr("disabled","disabled");
$.post('delete/apicollection', {
'api_collection_id': api_collection_id
}, function (response) {
location.reload();
});
});
});

View File

@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div id="api-collection-detail">
<h2>Add Endpoint to API Collection</h2>
<form class="form" action="" method="post">
{% csrf_token %}
<div class="row">
<div class="col-xs-12 col-md-4">
<div class="form-group">
{{ form.operation_id.label_tag}}
{{ form.operation_id }}
</div>
<input type="text" class="hidden" name="api-collection-id" value={{ api_collection_id }} >
<button type="submit" class="btn btn-primary btn-green">Add</button>
</div>
</div>
</form>
<h2>Endpoints</h2>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<th>Operation Ids</th>
</thead>
<tbody>
{% for api_collection_endpoint in api_collection_endpoints %}
<tr>
<td>{{ api_collection_endpoint.operation_id }}</td>
<td>
<form action="{% url 'delete-api-collection-endpoint' api_collection_endpoint.api_collection_id api_collection_endpoint.operation_id %}"
method="post">
{% csrf_token %}
<button type="submit" class="btn btn-primary btn-red">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,85 @@
{% extends 'base.html' %}
{% load static %}
{% block page_title %}{{ block.super }} / API Collections{% endblock page_title %}
{% block content %}
<h1>API Collections</h1>
<div class="row">
<div class="col-xs-12 col-sm-2">
<label class="form-group">API Collection Id:</label> <br>
</div>
<div class="col-xs-12 col-sm-2">
<label class="form-group">Collection Name:</label> <br>
</div>
<div class="col-xs-12 col-sm-2">
<label class="form-group">Is Sharable:</label> <br>
</div>
<div class="col-xs-12 col-sm-6">
<label class="form-group">Description:</label> <br>
</div>
</div>
<form method="post">
{% csrf_token %}
{% for api_collection in api_collections %}
{% url 'my-api-collection-detail' api_collection.api_collection_id as url_collection_detail %}
<div class="runner">
<div class="row">
<div class="col-xs-12 col-sm-2">
<div class="form-group" cols="1" rows="1">
<a class="api_collection_id" href="{{ url_collection_detail }}">{{ api_collection.api_collection_id }}</a></div>
</div>
{% if api_collection.api_collection_id %}
<div class="col-xs-12 col-sm-2">
<div class="form-group" cols="1" rows="1">
<div>{{ api_collection.api_collection_name }}</div></div>
</div>
<div class="col-xs-12 col-sm-2">
<div class="form-group" cols="1" rows="1">
<div>{{ api_collection.is_sharable }}</div>
</div>
</div>
<div class="col-xs-12 col-sm-4">
<div cols="40" rows="1" class="form-control">{{api_collection.description}}</div>
</div>
{% else %}
<div class="col-xs-12 col-sm-2">
<div class="form-group" cols="1" rows="1">
<input class="api_collection_name" value="Customer">
</div>
</div>
<div class="col-xs-12 col-sm-2">
<div class="form-group" cols="1" rows="1">
<select class="api_collection_is_sharable form-control">
<option value="True">True</option>
<option value="False">False</option>
</select>
</div>
</div>
<div class="col-xs-12 col-sm-4">
<textarea cols="40" rows="1" class="form-control api_collection_description">Describe the purpose of the collection</textarea>
</div>
{% endif %}
{% if forloop.counter0 == 0 %}
<div class="col-sm-12 col-sm-2">
<div class="form-group">
<button class="btn btn-primary btn-green forSave">Create </button>
</div>
</div>
{% endif %}
{% if forloop.counter0 > 0 %}
<div class="col-sm-12 col-sm-2">
<div class="form-group">
<button class="btn btn-primary btn-red forDelete">Delete</button>
</div>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</form>
{% endblock %}
{% block extrajs %}
<script type="text/javascript" src="{% static 'apicollections/js/apicollections.js' %}"></script>
{% endblock extrajs %}

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
URLs for config app
"""
from django.conf.urls import url
from apicollections.views import IndexView, apicollections_save, \
apicollections_delete, DetailView, DeleteCollectionEndpointView
urlpatterns = [
url(r'^$',
IndexView.as_view(),
name='apicollections-index'),
url(r'save/apicollection', apicollections_save,
name='apicollection-save'),
url(r'delete/apicollection', apicollections_delete,
name='apicollection-delete'),
url(r'^my-api-collection-ids/(?P<api_collection_id>[\w\@\.\+-]+)$',
DetailView.as_view(),
name='my-api-collection-detail'),
url(r'^delete/api-collections/(?P<api_collection_id>[\w-]+)/api-collection-endpoint/(?P<operation_id>[\w\@\.\+-]+)$',
DeleteCollectionEndpointView.as_view(),
name='delete-api-collection-endpoint'),
# url(r'^add/api-collections/(?P<api_collection_id>[\w-]+)/api-collection-endpoints/(?P<operation_id>[\w\@\.\+-]+)$',
# AddCollectionEndpointView.as_view(),
# name='add-api-collection-endpoint'),
]

View File

@ -0,0 +1,158 @@
# -*- coding: utf-8 -*-
"""
Views of config app
"""
import json
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect
from django.views.generic import FormView
from obp.api import API, APIError
from django.urls import reverse, reverse_lazy
from base.utils import exception_handle, error_once_only
from .forms import ApiCollectionsForm, ApiCollectionEndpointsForm
from django.views.decorators.csrf import csrf_exempt
class IndexView(LoginRequiredMixin, FormView):
"""Index view for config"""
template_name = "apicollections/index.html"
form_class = ApiCollectionsForm
success_url = reverse_lazy('apicollections-index')
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
api = API(self.request.session.get('obp'))
urlpath = '/my/api-collections'
api_collections =[]
try:
response = api.get(urlpath)
if 'code' in response and response['code'] >= 400:
error_once_only(self.request, response['message'])
else:
api_collections=response['api_collections']
except APIError as err:
error_once_only(self.request, Exception("OBP-API server is not running or do not response properly. "
"Please check OBP-API server. "
"Details: " + str(err)))
except BaseException as err:
error_once_only(self.request, (Exception("Unknown Error. Details:" + str(err))))
else:
# set the default endpoint there, the first item will be the new endpoint.
default_api_endpoint = {
"api_collection_name": "Customer",
"is_sharable": True,
"description":"Describe the purpose of the collection"
}
api_collections.insert(0,json.dumps(default_api_endpoint))
context.update({
'api_collections': api_collections
})
return context
class DetailView(LoginRequiredMixin, FormView):
"""Index view for config"""
template_name = "apicollections/detail.html"
form_class = ApiCollectionEndpointsForm
success_url = reverse_lazy('my-api-collection-detail')
def form_valid(self, form):
"""Posts api collection endpoint data to API"""
try:
data = form.cleaned_data
api = API(self.request.session.get('obp'))
api_collection_id = super(DetailView, self).get_context_data()['view'].kwargs['api_collection_id']
urlpath = '/my/api-collection-ids/{}/api-collection-endpoints'.format(api_collection_id)
payload = {
'operation_id': data['operation_id']
}
api_collection_endpoint = api.post(urlpath, payload=payload)
except APIError as err:
messages.error(self.request, err)
return super(DetailView, self).form_invalid(form)
except:
messages.error(self.request, 'Unknown Error')
return super(DetailView, self).form_invalid(form)
if 'code' in api_collection_endpoint and api_collection_endpoint['code']>=400:
messages.error(self.request, api_collection_endpoint['message'])
return super(DetailView, self).form_invalid(form)
else:
msg = 'Operation Id {} has been added.'.format(data['operation_id'])
messages.success(self.request, msg)
self.success_url = self.request.path
return super(DetailView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
api_collection_id = context['view'].kwargs['api_collection_id']
api = API(self.request.session.get('obp'))
urlpath = '/my/api-collection-ids/{}/api-collection-endpoints'.format(api_collection_id)
api_collection_endpoints =[]
try:
response = api.get(urlpath)
if 'code' in response and response['code'] >= 400:
error_once_only(self.request, response['message'])
else:
api_collection_endpoints=response['api_collection_endpoints']
except APIError as err:
error_once_only(self.request, Exception("OBP-API server is not running or do not response properly. "
"Please check OBP-API server. "
"Details: " + str(err)))
except BaseException as err:
error_once_only(self.request, (Exception("Unknown Error. Details:" + str(err))))
else:
context.update({
'api_collection_endpoints': api_collection_endpoints,
'api_collection_id': api_collection_id
})
return context
class DeleteCollectionEndpointView(LoginRequiredMixin, FormView):
"""View to delete an api collection endpoint"""
def post(self, request, *args, **kwargs):
"""Deletes api collection endpoint from API"""
api = API(self.request.session.get('obp'))
try:
urlpath = '/my/api-collections-ids/{}/api-collection-endpoints/{}'\
.format(kwargs['api_collection_id'],kwargs['operation_id'])
result = api.delete(urlpath)
if result is not None and 'code' in result and result['code']>=400:
messages.error(request, result['message'])
else:
msg = 'Operation Id {} has been deleted.'.format(kwargs['operation_id'])
messages.success(request, msg)
except APIError as err:
messages.error(request, err)
except:
messages.error(self.request, 'Unknown Error')
redirect_url = reverse('my-api-collection-detail',kwargs={"api_collection_id":kwargs['api_collection_id']})
return HttpResponseRedirect(redirect_url)
@exception_handle
@csrf_exempt
def apicollections_save(request):
api = API(request.session.get('obp'))
urlpath = '/my/api-collections'
payload = {
'api_collection_name': request.POST.get('api_collection_name').strip(),
'is_sharable': bool(request.POST.get('api_collection_is_sharable')),
'description': request.POST.get('api_collection_description').strip()
}
result = api.post(urlpath, payload = payload)
return result
@exception_handle
@csrf_exempt
def apicollections_delete(request):
api_collection_id = request.POST.get('api_collection_id').strip()
api = API(request.session.get('obp'))
urlpath = '/my/api-collections/{}'.format(api_collection_id)
result = api.delete(urlpath)
return result

View File

@ -58,7 +58,8 @@ INSTALLED_APPS = [
'config',
'webui',
'methodrouting',
'dynamicendpoints'
'dynamicendpoints',
'apicollections'
]
MIDDLEWARE = [

View File

@ -40,4 +40,5 @@ urlpatterns = [
url(r'^webui/', include('webui.urls')),
url(r'^methodrouting/', include('methodrouting.urls')),
url(r'^dynamicendpoints/', include('dynamicendpoints.urls')),
url(r'^apicollections/', include('apicollections.urls')),
]

View File

@ -70,13 +70,15 @@
{% url "webui-index" as webui_props_index_url %}
{% url "methodrouting-index" as methodrouting_index_url %}
{% url "dynamicendpoints-index" as dynamic_endpoints_index_url %}
{% url "apicollections-index" as api_collections_index_url %}
<li class="dropdown{% if config_index_url in request.path %} active{% endif %}">
<a href="#" data-toggle="dropdown" class="dropdown-toggle">Configurations</a>
<ul class="dropdown-menu">
<li{% if config_index_url in request.path %} class="active"{% endif %}><a href="{{ config_index_url }}">Config</a></li><hr class="dropdown-hr">
<li{% if webui_props_index_url in request.path %} class="active"{% endif %}><a href="{{ webui_props_index_url }}">Webui Props</a></li><hr class="dropdown-hr">
<li{% if methodrouting_index_url in request.path %} class="active"{% endif %}><a href="{{ methodrouting_index_url }}">Method Routings</a></li><hr class="dropdown-hr">
<li{% if dynamic_endpoints_index_url in request.path %} class="active"{% endif %}><a href="{{ dynamic_endpoints_index_url }}">Dynamic Endpoints</a></li>
<li{% if dynamic_endpoints_index_url in request.path %} class="active"{% endif %}><a href="{{ dynamic_endpoints_index_url }}">Dynamic Endpoints</a></li><hr class="dropdown-hr">
<li{% if api_collections_index_url in request.path %} class="active"{% endif %}><a href="{{ api_collections_index_url }}">My API Collections</a></li>
</ul>
</li>