From ab7a2b6b11b8a1b1dda1230ad5db96e91e43a0e2 Mon Sep 17 00:00:00 2001 From: mirkoperillo Date: Mon, 1 Oct 2018 06:28:38 +0200 Subject: [PATCH] fix #27 --- src/css/resting.css | 47 +++++++++++++++++- src/index.html | 33 ++++++++++++- src/js/app.js | 111 ++++++++++++++++++++++++++++++++++++------ src/js/app/storage.js | 30 +++++++++--- src/manifest.json | 2 +- 5 files changed, 197 insertions(+), 26 deletions(-) diff --git a/src/css/resting.css b/src/css/resting.css index 8f71bf6..b09ea4a 100644 --- a/src/css/resting.css +++ b/src/css/resting.css @@ -36,10 +36,17 @@ body { z-index: 100; } +.context-dialog { + position: absolute; + top: 100px; + width: 700px; + z-index: 100; +} + .bookmark { display: block; padding: 10px 15px; - cursor: pointer; + cursor: pointer; } .folder { @@ -54,7 +61,7 @@ a.bookmark, a.folder { } a.folder:hover, a.bookmark:hover, .folder .panel-heading:hover { - background-color: #337ab7; + background-color: #337ab7; color: #fff; } @@ -82,3 +89,39 @@ label.disabled { color: gray; } +.dropdown-submenu{ position: relative; } + +.dropdown-submenu>.dropdown-menu{ + top:0; + left:100%; + margin-top:-6px; + margin-left:-1px; + -webkit-border-radius:0 6px 6px 6px; + -moz-border-radius:0 6px 6px 6px; + border-radius:0 6px 6px 6px; +} + +.dropdown-submenu>a:after{ + display:block; + content:" "; + float:right; + width:0; + height:0; + border-color:transparent; + border-style:solid; + border-width:5px 0 5px 5px; + border-left-color:#cccccc; + margin-top:5px;margin-right:-10px; +} +.dropdown-submenu:hover>a:after{ + border-left-color:#555; +} +.dropdown-submenu.pull-left{ float: none; } +.dropdown-submenu.pull-left>.dropdown-menu{ + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + diff --git a/src/index.html b/src/index.html index bbf3308..8dc203a 100644 --- a/src/index.html +++ b/src/index.html @@ -39,6 +39,15 @@
-

Resting v0.7.3

+

Resting v0.8.0

Project Website -- Issue tracker
@@ -213,6 +222,28 @@

Resting v0.7.3

+
+
+ Context +
+
+

+ Use variables as {var_name} in +

    +
  • the request URL
  • +
  • value of header
  • +
  • part of the body
  • +
  • value of querystring
  • +
  • as username and password in BASIC authentication
  • +
+

+ +
+ +
+ diff --git a/src/js/app.js b/src/js/app.js index 4a50134..516b904 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -12,6 +12,13 @@ requirejs.config({ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','app/request','app/bookmark','bootstrap'], function($,storage,ko,ksb,hjls,request,makeBookmarkProvider, bootstrap) { + function ContextVm(createDefault) { + const self = this; + this.name = ko.observable(createDefault ? 'default' : ''); + this.variables = ko.observableArray(); + this.isDefault = createDefault; + }; + function RequestVm(request = {}) { const self = this; this.method = ko.observable(''); @@ -49,6 +56,8 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a function AppViewModel() { const Resting = { + contexts : new ContextVm(true), + bookmarkSelected : new BookmarkSelectedVm(), requestSelected : new RequestVm(), responseContent : {}, @@ -92,13 +101,14 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a showBookmarkDeleteDialog: ko.observable(false), showAboutDialog: ko.observable(false), showCreditsDialog: ko.observable(false), + showContextDialog: ko.observable(false), }; const bookmarkProvider = makeBookmarkProvider(storage); - const convertToFormData = (data = []) => + const convertToFormData = (data = [], context = {}) => data.filter(param => param.enabled()).reduce((acc, record) => { - acc[record.name()] = record.value(); + acc[record.name()] = _applyContext(record.value(),context); return acc; }, {}); @@ -114,6 +124,10 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a Resting.showCreditsDialog(true); }; + const contextDialog = () => { + Resting.showContextDialog(true); + }; + const dismissCreditsDialog = () => { Resting.showCreditsDialog(false); }; @@ -122,8 +136,12 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a Resting.showAboutDialog(false); }; - const convertToUrlEncoded = (data = []) => - data.filter(param => param.enabled()).map( param => `${param.name()}=${param.value()}`).join('&'); + const dismissContextDialog = () => { + Resting.showContextDialog(false); + }; + + const convertToUrlEncoded = (data = [], context) => + data.filter(param => param.enabled()).map( param => `${param.name()}=${_applyContext(param.value(),context)}`).join('&'); const updateBody = (bodyType, body) => { clearRequestBody(); @@ -197,17 +215,20 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a } }; - const dataToSend = () => { + const dataToSend = (context) => { if (Resting.bodyType() === 'form-data') { - return convertToFormData(Resting.formDataParams()); + return convertToFormData(Resting.formDataParams(),context); } else if (Resting.bodyType() === 'x-www-form-urlencoded') { - return convertToUrlEncoded(Resting.formEncodedParams()); + return convertToUrlEncoded(Resting.formEncodedParams(), context); } else if (Resting.bodyType() === 'raw') { - return Resting.rawBody().trim(); + return _applyContext(Resting.rawBody().trim(),context); } }; - const _authentication = () => ({type: Resting.authenticationType(), username: Resting.username(), password: Resting.password()}); + const _applyContextToArray = (a = [], context = {}) => { + return + }; + const _authentication = (context = {}) => ({type: Resting.authenticationType(), username: _applyContext(Resting.username(),context), password: _applyContext(Resting.password(),context)}); const body = (bodyType) => { if (bodyType === 'form-data') { @@ -346,9 +367,9 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a } }; - const convertToHeaderObj = headersList => + const convertToHeaderObj = (headersList = [], context = {}) => headersList.filter(header => header.enabled()).reduce((acc, header) => { - acc[header.name()] = header.value(); + acc[header.name()] = _applyContext(header.value(),context); return acc; }, {}); @@ -367,16 +388,44 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a } }; - const _convertToQueryString = (params = []) => { - return params.filter(param => param.enabled()).map( param => ({name: param.name(), value: param.value()})); + const _convertToQueryString = (params = [], context = {}) => { + return params.filter(param => param.enabled()).map( param => ({name: param.name(), value: _applyContext(param.value(), context)})); }; const send = () => { + const mapping = _mapContext(); if(Resting.requestSelected.url() && Resting.requestSelected.url().trim().length > 0) { clearResponse(); - request.execute(Resting.requestSelected.method(),Resting.requestSelected.url(),convertToHeaderObj(Resting.requestHeaders()), _convertToQueryString(Resting.querystring()), Resting.bodyType(),Resting.dataToSend(), - _authentication(),displayResponse); + const url = _applyContext(Resting.requestSelected.url(),mapping); + request.execute(Resting.requestSelected.method(),url,convertToHeaderObj(Resting.requestHeaders(), mapping), _convertToQueryString(Resting.querystring(), mapping), Resting.bodyType(),Resting.dataToSend(mapping), + _authentication(mapping),displayResponse); + } + }; + + const _mapContext = () => { + const mapping = {}; + Resting.contexts.variables().filter(v => v.enabled()).forEach( v => mapping[v.name()] = v.value()); + return mapping; + }; + + const _applyContext = (value = '',context = {}) => { + const tokens = _tokenize(value); + let computed = value.slice(0); + if(tokens) { + tokens.forEach(t => { + const contextVar = t.substring(1,t.length-1); + if(context[contextVar]) { + computed = computed.replace(t, context[contextVar]); + } + }); } + return computed; + }; + + const _tokenize = (v = '') => { + const varRegexp = /\{\w+\}/g; + const tokens = v.match(varRegexp); + return tokens; }; const loadBookmarkInView = (bookmark = {}) => { @@ -471,6 +520,19 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a } }; + const saveContext = () => { + storage.saveContext({name : Resting.contexts.name(), variables : _extractModelFromVM(Resting.contexts.variables()) }); + dismissContextDialog(); + }; + + const loadContexts = () => { + // load contexts + storage.loadContexts( ctx => { + Resting.contexts.variables(_convertToEntryItemVM(ctx.variables)); + }); + }; + + Resting.parseRequest = parseRequest; Resting.dataToSend = dataToSend; Resting.send = send; @@ -491,8 +553,11 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a Resting.reset = reset; Resting.aboutDialog = aboutDialog; Resting.creditsDialog = creditsDialog; + Resting.contextDialog = contextDialog; Resting.dismissCreditsDialog = dismissCreditsDialog; Resting.dismissAboutDialog = dismissAboutDialog; + Resting.dismissContextDialog = dismissContextDialog; + Resting.saveContext = saveContext; // FIXME: not good to expose this internal function Resting._saveBookmark = _saveBookmark; @@ -500,6 +565,8 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a Resting.loadBookmarkInView = loadBookmarkInView; Resting.isBookmarkLoaded = isBookmarkLoaded; Resting.bookmarkScreenName = bookmarkScreenName; + Resting.loadContexts = loadContexts; + return Resting; } @@ -531,6 +598,7 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a }); + // Show all options, more restricted setup than the Knockout regular binding. var options = { attribute: "data-bind", // default "data-sbind" @@ -541,6 +609,17 @@ requirejs(['jquery','app/storage','knockout','knockout-secure-binding','hjls','a ko.bindingProvider.instance = new ksb(options); - ko.applyBindings(new AppViewModel()); + const appVM = new AppViewModel(); + ko.applyBindings(appVM); + + $('ul.dropdown-menu [data-toggle=dropdown]').on('click', function(event) { + event.preventDefault(); + event.stopPropagation(); + $(this).parent().siblings().removeClass('open'); + $(this).parent().toggleClass('open'); + }); + + appVM.loadContexts(); + }); }); diff --git a/src/js/app/storage.js b/src/js/app/storage.js index 1b5ee94..4037762 100644 --- a/src/js/app/storage.js +++ b/src/js/app/storage.js @@ -4,11 +4,16 @@ define(['localforage'],function(localforage){ name: 'resting', storeName: 'bookmarks', }); - + + const _contextsStore = localforage.createInstance({ + name: 'resting', + storeName: 'contexts', + }); + const deleteById = (id, callback) => { localforage.removeItem(id, callback); }; - + const save = (bookmark) => { if(!bookmark.id) { return { result: 'KO', message: 'id must be set'}; @@ -16,22 +21,35 @@ define(['localforage'],function(localforage){ localforage.setItem(bookmark.id, bookmark); return { result: 'OK', message: ''}; }; - + const iterate = (callback, callbackResult) => { if(!callbackResult) { - localforage.iterate(function(value,key,iterationNumber) { + localforage.iterate(function(value,key,iterationNumber) { callback(value); }); } else { - localforage.iterate(function(value,key,iterationNumber) { + localforage.iterate(function(value,key,iterationNumber) { callback(value); }, callbackResult); }; }; - + + const saveContext = (context) => { + _contextsStore.setItem(context.name, context); + return { result: 'OK', message: ''}; + }; + + const loadContexts = (callback) => { + _contextsStore.iterate(function(value,key,iterationNumber) { + callback(value); + }); + }; + return { save : save, deleteById : deleteById, iterate : iterate, + saveContext, + loadContexts, }; }); diff --git a/src/manifest.json b/src/manifest.json index ebcdca2..658465c 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "description": "rest client focused on organization of the saved requests", "manifest_version": 2, "name": "Resting", - "version": "0.7.3", + "version": "0.8.0", "author" : "Mirko Perillo", "homepage_url": "https://github.com/mirkoperillo/resting", "icons": {