Skip to content

Commit 09b4429

Browse files
Added search (#96)
* added search and unit tests Co-authored-by: Karissa Jacobsen <karissa.jacobsen@docusign.com>
1 parent 7f41344 commit 09b4429

File tree

188 files changed

+1651
-688
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

188 files changed

+1651
-688
lines changed

Gemfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ group :test do
6565
gem 'selenium-webdriver', '~> 3.142.7'
6666
# Easy installation and use of chromedriver to run system tests with Chrome
6767
gem 'chromedriver-helper', '~> 2.1.1'
68+
gem 'test-unit'
6869
end
6970

7071
gem 'docusign_admin', '~> 1.1.0'
7172
gem 'docusign_click', '~> 1.2.2'
72-
gem 'docusign_esign', '~> 3.19.0'
73+
gem 'docusign_esign', '~> 3.20.0'
7374
gem 'docusign_monitor', '~> 1.1.0'
7475
gem 'docusign_rooms', '~> 1.2.0.rc1'
7576
gem 'omniauth-oauth2', '~> 1.7.1'
@@ -78,3 +79,5 @@ gem 'omniauth-rails_csrf_protection'
7879
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
7980
gem 'tzinfo-data', '~> 1.2022.1', '>= 1.2022.1'
8081
gem 'wdm', '>= 0.1.0', platforms: %i[mingw mswin x64_mingw]
82+
83+
gem "matrix", "~> 0.4.2"

Gemfile.lock

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ GEM
108108
json (~> 2.1, >= 2.1.0)
109109
jwt (~> 2.2, >= 2.2.1)
110110
typhoeus (~> 1.0, >= 1.0.1)
111-
docusign_esign (3.19.0)
111+
docusign_esign (3.20.0)
112112
addressable (~> 2.7, >= 2.7.0)
113113
json (~> 2.1, >= 2.1.0)
114114
jwt (~> 2.2, >= 2.2.1)
@@ -153,6 +153,7 @@ GEM
153153
mail (2.7.1)
154154
mini_mime (>= 0.1.1)
155155
marcel (1.0.2)
156+
matrix (0.4.2)
156157
method_source (0.9.2)
157158
mini_mime (1.1.2)
158159
minitest (5.16.3)
@@ -193,6 +194,7 @@ GEM
193194
parallel (1.22.1)
194195
parser (3.1.2.1)
195196
ast (~> 2.4.1)
197+
power_assert (2.0.1)
196198
pry (0.12.2)
197199
coderay (~> 1.1.0)
198200
method_source (~> 0.9.0)
@@ -285,6 +287,8 @@ GEM
285287
activesupport (>= 5.2)
286288
sprockets (>= 3.0.0)
287289
sqlite3 (1.4.4)
290+
test-unit (3.5.3)
291+
power_assert
288292
thor (1.2.1)
289293
tilt (2.0.11)
290294
timeout (0.3.0)
@@ -301,7 +305,6 @@ GEM
301305
execjs (>= 0.3.0, < 3)
302306
unicode-display_width (2.3.0)
303307
version_gem (1.1.1)
304-
wdm (0.1.1)
305308
web-console (4.2.0)
306309
actionview (>= 6.0.0)
307310
activemodel (>= 6.0.0)
@@ -327,11 +330,12 @@ DEPENDENCIES
327330
coffee-rails (~> 5.0.0)
328331
docusign_admin (~> 1.1.0)
329332
docusign_click (~> 1.2.2)
330-
docusign_esign (~> 3.19.0)
333+
docusign_esign (~> 3.20.0)
331334
docusign_monitor (~> 1.1.0)
332335
docusign_rooms (~> 1.2.0.rc1)
333336
jbuilder (~> 2.11.5)
334337
listen (~> 3.7.1)
338+
matrix (~> 0.4.2)
335339
omniauth-oauth2 (~> 1.7.1)
336340
omniauth-rails_csrf_protection
337341
pry-nav (~> 0.3.0)
@@ -344,6 +348,7 @@ DEPENDENCIES
344348
spring (~> 2.1.0)
345349
spring-watcher-listen (~> 2.0.1)
346350
sqlite3 (~> 1.4.4)
351+
test-unit
347352
turbolinks (~> 5.2.1)
348353
tzinfo-data (~> 1.2022.1, >= 1.2022.1)
349354
uglifier (~> 4.2.0)

app/assets/javascripts/search.js

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
const DS_SEARCH = (function () {
2+
const API_TYPES = {
3+
ESIGNATURE: "esignature",
4+
MONITOR: "monitor",
5+
CLICK: "click",
6+
ROOMS: "rooms",
7+
ADMIN: "admin",
8+
};
9+
10+
const processJSONData = function () {
11+
const json_raw = $("#api_json_data").text();
12+
const json = json_raw ? JSON.parse(json_raw) : false;
13+
14+
return json;
15+
}
16+
17+
let processCFR11Value = function () {
18+
let json_raw = $("#cfr11_data").text();
19+
20+
return json_raw;
21+
}
22+
23+
function checkIfExampleMatches(example, matches) {
24+
const name = example.ExampleName;
25+
const description = example.ExampleDescription;
26+
const pathNames = example.LinksToAPIMethod.map((a) => a.PathName);
27+
28+
for (let i = 0; i < matches.length; i++) {
29+
if (
30+
name === matches[i].value ||
31+
description === matches[i].value ||
32+
pathNames.indexOf(matches[i].value) > -1
33+
) {
34+
return true;
35+
}
36+
}
37+
38+
return false;
39+
}
40+
41+
function clearNonMatchingExamples(examples, matches) {
42+
for (let i = examples.length - 1; i >= 0; i--) {
43+
if (!checkIfExampleMatches(examples[i], matches)) {
44+
examples.splice(i, 1);
45+
}
46+
}
47+
}
48+
49+
function clearResultsAfterMatching(api, matches) {
50+
const groups = api.Groups;
51+
52+
for (let i = groups.length - 1; i >= 0; i--) {
53+
const group = groups[i];
54+
clearNonMatchingExamples(group.Examples, matches);
55+
56+
if (group.Examples.length === 0) {
57+
groups.splice(i, 1);
58+
}
59+
}
60+
}
61+
62+
const findCodeExamplesByKeywords = function(json, pattern) {
63+
const options = {
64+
isCaseSensitive: false,
65+
threshold: -0.0,
66+
includeMatches: true,
67+
ignoreLocation: true,
68+
useExtendedSearch: true,
69+
keys: [
70+
"Groups.Examples.ExampleName",
71+
"Groups.Examples.ExampleDescription",
72+
"Groups.Examples.LinksToAPIMethod.PathName",
73+
],
74+
};
75+
76+
let clearJSON = JSON.stringify(json).replace(/<\/?[^>]+(>|$)/g, "");
77+
const fuse = new Fuse(JSON.parse(clearJSON), options);
78+
79+
var searchResults = fuse.search(JSON.stringify(pattern));
80+
81+
searchResults.forEach(searchResult => {
82+
return clearResultsAfterMatching(searchResult.item, searchResult.matches)
83+
});
84+
85+
return searchResults;
86+
}
87+
88+
const getExamplesByAPIType = function (apiType, codeExamples) {
89+
let codeExamplesByAPI = codeExamples.find(
90+
(x) => x.Name.toLowerCase() === apiType
91+
);
92+
93+
if (codeExamplesByAPI != null) {
94+
return [codeExamplesByAPI];
95+
} else {
96+
return null;
97+
}
98+
};
99+
100+
const getEnteredAPIType = function (inputValue) {
101+
const inputLength = inputValue.length;
102+
103+
for (const key in API_TYPES) {
104+
if (Object.hasOwnProperty.call(API_TYPES, key)) {
105+
const apiType = API_TYPES[key];
106+
const comparedValue = apiType.substr(0, inputLength);
107+
108+
if (inputValue === comparedValue) {
109+
return apiType;
110+
}
111+
}
112+
}
113+
114+
return null;
115+
};
116+
117+
function getLinkForApiType(apiName) {
118+
switch (apiName) {
119+
case API_TYPES.ADMIN:
120+
return "aeg";
121+
case API_TYPES.CLICK:
122+
return "ceg";
123+
case API_TYPES.ROOMS:
124+
return "reg";
125+
case API_TYPES.MONITOR:
126+
return "meg";
127+
case API_TYPES.ESIGNATURE:
128+
return "eg";
129+
}
130+
}
131+
132+
let addCodeExampleToHomepage = function (codeExamples) {
133+
var cfrPart11 = processCFR11Value();
134+
135+
codeExamples.forEach(
136+
element => {
137+
let linkToCodeExample = getLinkForApiType(element.Name.toLowerCase());
138+
139+
element.Groups.forEach(
140+
group => {
141+
$("#filtered_code_examples").append("<h2>" + group.Name + "</h2>");
142+
143+
group.Examples.forEach(
144+
example => {
145+
if (!example.SkipForLanguages || !example.SkipForLanguages.toLowerCase().includes("c#")) {
146+
if (element.Name.toLowerCase() !== API_TYPES.ESIGNATURE.toLowerCase() ||
147+
((example.CFREnabled == "AllAccounts") ||
148+
((cfrPart11 == "enabled") && (example.CFREnabled == "CFROnly")) ||
149+
((cfrPart11 != "enabled") && (example.CFREnabled == "NonCFR")))) {
150+
$("#filtered_code_examples").append(
151+
"<h4 id="
152+
+ "example".concat("0".repeat(3 - example.ExampleNumber.toString().length)).concat(example.ExampleNumber) + ">"
153+
+ "<a href = "
154+
+ linkToCodeExample.concat("0".repeat(3 - example.ExampleNumber.toString().length)).concat(example.ExampleNumber)
155+
+ " >"
156+
+ example.ExampleName
157+
+ "</a ></h4 >"
158+
);
159+
160+
$("#filtered_code_examples").append("<p>" + example.ExampleDescription + "</p>");
161+
162+
$("#filtered_code_examples").append("<p>");
163+
164+
if (example.LinksToAPIMethod.length == 1) {
165+
$("#filtered_code_examples").append(processJSONData().SupportingTexts.APIMethodUsed);
166+
}
167+
else {
168+
$("#filtered_code_examples").append(processJSONData().SupportingTexts.APIMethodUsedPlural);
169+
}
170+
171+
for (let index = 0; index < example.LinksToAPIMethod.length; index++) {
172+
$("#filtered_code_examples").append(
173+
" <a target='_blank' href='"
174+
+ example.LinksToAPIMethod[index].Path
175+
+ "'>"
176+
+ example.LinksToAPIMethod[index].PathName
177+
+ "</a>"
178+
);
179+
180+
if (index + 1 === example.LinksToAPIMethod.length) {
181+
$("#filtered_code_examples").append("<span></span>");
182+
}
183+
else if (index + 1 === example.LinksToAPIMethod.length - 1) {
184+
$("#filtered_code_examples").append("<span> and </span>");
185+
}
186+
else {
187+
$("#filtered_code_examples").append("<span>, </span>");
188+
}
189+
190+
}
191+
192+
$("#filtered_code_examples").append("</p> ");
193+
}
194+
}
195+
}
196+
);
197+
}
198+
);
199+
}
200+
);
201+
}
202+
203+
const textCouldNotBeFound = function () {
204+
$("#filtered_code_examples").append(
205+
processJSONData().SupportingTexts.SearchFailed
206+
);
207+
};
208+
209+
return {
210+
processJSONData: processJSONData,
211+
getEnteredAPIType: getEnteredAPIType,
212+
getExamplesByAPIType: getExamplesByAPIType,
213+
findCodeExamplesByKeywords: findCodeExamplesByKeywords,
214+
textCouldNotBeFound: textCouldNotBeFound,
215+
addCodeExampleToHomepage: addCodeExampleToHomepage,
216+
};
217+
})();
218+
219+
const input = document.getElementById("code_example_search");
220+
const log = document.getElementById("values");
221+
222+
input.addEventListener("input", updateValue);
223+
224+
function updateValue(esearchPattern) {
225+
document.getElementById("filtered_code_examples").innerHTML = "";
226+
227+
const inputValue = esearchPattern.target.value.toLowerCase();
228+
const json = DS_SEARCH.processJSONData().APIs;
229+
230+
if (inputValue === "") {
231+
DS_SEARCH.addCodeExampleToHomepage(json);
232+
} else {
233+
const apiType = DS_SEARCH.getEnteredAPIType(inputValue);
234+
235+
if (apiType !== null) {
236+
const adminExamples = DS_SEARCH.getExamplesByAPIType(apiType, json);
237+
DS_SEARCH.addCodeExampleToHomepage(adminExamples);
238+
} else {
239+
const result = DS_SEARCH.findCodeExamplesByKeywords(json, inputValue);
240+
if (result.length < 1) {
241+
DS_SEARCH.textCouldNotBeFound();
242+
} else {
243+
result.forEach(x => {
244+
var api = json.filter(api => {
245+
return api.Name === x.item.Name;
246+
})[0];
247+
248+
x.item.Groups.forEach((group, groupIndex) => {
249+
var unfilteredGroup = api.Groups.filter(apiGroup => {
250+
return apiGroup.Name === group.Name;
251+
})[0];
252+
253+
group.Examples.forEach((example, index) => {
254+
var clearedExample = unfilteredGroup.Examples.filter(apiExample => {
255+
return apiExample.ExampleNumber === example.ExampleNumber;
256+
})[0];
257+
x.item.Groups[groupIndex].Examples[index] = clearedExample;
258+
});
259+
});
260+
261+
DS_SEARCH.addCodeExampleToHomepage([x.item]);
262+
});
263+
}
264+
}
265+
}
266+
}

app/controllers/admin_api/eg001_create_user_controller.rb renamed to app/controllers/admin_api/aeg001_create_user_controller.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
class AdminApi::Eg001CreateUserController < EgController
1+
class AdminApi::Aeg001CreateUserController < EgController
22
include ApiCreator
3-
before_action :check_auth
4-
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1) }
3+
before_action -> { check_auth('Admin') }
4+
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'Admin') }
55

66
def create
77
args = {

app/controllers/admin_api/eg002_create_active_clm_esign_user_controller.rb renamed to app/controllers/admin_api/aeg002_create_active_clm_esign_user_controller.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
class AdminApi::Eg002CreateActiveClmEsignUserController < EgController
2-
before_action :check_auth
3-
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 2) }
1+
class AdminApi::Aeg002CreateActiveClmEsignUserController < EgController
2+
before_action -> { check_auth('Admin') }
3+
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 2, 'Admin') }
44

55
def create
66
args = {

app/controllers/admin_api/eg003_bulk_export_user_data_controller.rb renamed to app/controllers/admin_api/aeg003_bulk_export_user_data_controller.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
class AdminApi::Eg003BulkExportUserDataController < EgController
2-
before_action :check_auth
3-
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 3) }
1+
class AdminApi::Aeg003BulkExportUserDataController < EgController
2+
before_action -> { check_auth('Admin') }
3+
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 3, 'Admin') }
44

55
def create
66
session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil?

app/controllers/admin_api/eg004_import_user_controller.rb renamed to app/controllers/admin_api/aeg004_import_user_controller.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
class AdminApi::Eg004ImportUserController < EgController
2-
before_action :check_auth
3-
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 4) }
1+
class AdminApi::Aeg004ImportUserController < EgController
2+
before_action -> { check_auth('Admin') }
3+
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 4, 'Admin') }
44

55
def create
66
session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil?

0 commit comments

Comments
 (0)