diff --git a/apimanager/apicollections/__init__.py b/apimanager/apicollections/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/apimanager/apicollections/apps.py b/apimanager/apicollections/apps.py
new file mode 100644
index 0000000..c4ab07c
--- /dev/null
+++ b/apimanager/apicollections/apps.py
@@ -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'
diff --git a/apimanager/apicollections/forms.py b/apimanager/apicollections/forms.py
new file mode 100644
index 0000000..936c630
--- /dev/null
+++ b/apimanager/apicollections/forms.py
@@ -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
+ )
\ No newline at end of file
diff --git a/apimanager/apicollections/static/apicollections/js/apicollections.js b/apimanager/apicollections/static/apicollections/js/apicollections.js
new file mode 100644
index 0000000..d0d9d94
--- /dev/null
+++ b/apimanager/apicollections/static/apicollections/js/apicollections.js
@@ -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();
+ });
+ });
+});
diff --git a/apimanager/apicollections/templates/apicollections/detail.html b/apimanager/apicollections/templates/apicollections/detail.html
new file mode 100644
index 0000000..fe7bcd7
--- /dev/null
+++ b/apimanager/apicollections/templates/apicollections/detail.html
@@ -0,0 +1,43 @@
+{% extends 'base.html' %}
+{% load static %}
+{% block content %}
+
+
Add Endpoint to API Collection
+
+
+
Endpoints
+
+
+
+ | Operation Ids |
+
+
+ {% for api_collection_endpoint in api_collection_endpoints %}
+
+ | {{ api_collection_endpoint.operation_id }} |
+
+
+ |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/apimanager/apicollections/templates/apicollections/index.html b/apimanager/apicollections/templates/apicollections/index.html
new file mode 100644
index 0000000..ae552b5
--- /dev/null
+++ b/apimanager/apicollections/templates/apicollections/index.html
@@ -0,0 +1,85 @@
+{% extends 'base.html' %}
+{% load static %}
+{% block page_title %}{{ block.super }} / API Collections{% endblock page_title %}
+
+{% block content %}
+ API Collections
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+
+{% block extrajs %}
+
+{% endblock extrajs %}
diff --git a/apimanager/apicollections/urls.py b/apimanager/apicollections/urls.py
new file mode 100644
index 0000000..84fbe65
--- /dev/null
+++ b/apimanager/apicollections/urls.py
@@ -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[\w\@\.\+-]+)$',
+ DetailView.as_view(),
+ name='my-api-collection-detail'),
+ url(r'^delete/api-collections/(?P[\w-]+)/api-collection-endpoint/(?P[\w\@\.\+-]+)$',
+ DeleteCollectionEndpointView.as_view(),
+ name='delete-api-collection-endpoint'),
+ # url(r'^add/api-collections/(?P[\w-]+)/api-collection-endpoints/(?P[\w\@\.\+-]+)$',
+ # AddCollectionEndpointView.as_view(),
+ # name='add-api-collection-endpoint'),
+]
diff --git a/apimanager/apicollections/views.py b/apimanager/apicollections/views.py
new file mode 100644
index 0000000..743a857
--- /dev/null
+++ b/apimanager/apicollections/views.py
@@ -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
diff --git a/apimanager/apimanager/settings.py b/apimanager/apimanager/settings.py
index 632903d..abdcb1e 100644
--- a/apimanager/apimanager/settings.py
+++ b/apimanager/apimanager/settings.py
@@ -58,7 +58,8 @@ INSTALLED_APPS = [
'config',
'webui',
'methodrouting',
- 'dynamicendpoints'
+ 'dynamicendpoints',
+ 'apicollections'
]
MIDDLEWARE = [
diff --git a/apimanager/apimanager/urls.py b/apimanager/apimanager/urls.py
index 01e4db3..3fba52b 100644
--- a/apimanager/apimanager/urls.py
+++ b/apimanager/apimanager/urls.py
@@ -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')),
]
diff --git a/apimanager/base/templates/base.html b/apimanager/base/templates/base.html
index 7533831..c28ea9e 100644
--- a/apimanager/base/templates/base.html
+++ b/apimanager/base/templates/base.html
@@ -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 %}
Configurations