Skip to content

Commit

Permalink
✨Implemented Fuzzy Search through an API on the Dev Dashboard (#19676)
Browse files Browse the repository at this point in the history
* Created an API for dashboard, and a fuzzy search endpoint

* Got the client side of fuzzy searching working

* Got fuzzy searching working

* Still working on fuzzy finding

* Got debounced input

* Finished up fuzzy search for files

* Finished dashboard fuzzy finding

* Fixed the space between column styling

* Fixed linting errors

* Search box styling, structure

* naming

* Fixed huge rows, and tests

* Made the rows and columns the same sizes as they were originally
  • Loading branch information
torch2424 committed Dec 19, 2018
1 parent 1393fe8 commit 96d8931
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 137 deletions.
78 changes: 78 additions & 0 deletions build-system/app-index/api/api.js
@@ -0,0 +1,78 @@
/**
* Copyright 2018 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';

const Fuse = require('fuse.js');
const {
getListing,
} = require('../util/listing');

async function badRequest(res) {
res.status(400).end();
}

async function searchListing(root, req, res) {

if (!req.query || !req.query.path) {
badRequest(res);
return;
}

const basepath = req.query.path;
const fileSet = await getListing(root, basepath);

if (!fileSet) {
badRequest(res);
return;
}

if (!req.query.search) {
res.status(200).json(fileSet);
return;
}

// Fuzzy find from the file set
const fuse = new Fuse(fileSet, {
shouldSort: true,
threshold: 0.4,
});
const foundFileIndexes = fuse.search(req.query.search);

const response = [];
foundFileIndexes.forEach(fileIndex => {
response.push(fileSet[fileIndex]);
});



res.status(200).json(response);
}

// Function to handle API Requests
// Returns true is handled, false otherwise
async function handleApiRequest(root, req, res, next) {

if (req.path.includes('listing')) {
await searchListing(root, req, res);
return;
}

next();
}

module.exports = {
handleApiRequest,
};
59 changes: 13 additions & 46 deletions build-system/app-index/index.js
Expand Up @@ -15,11 +15,16 @@
*/
'use strict';


const BBPromise = require('bluebird');
const bundler = require('./bundler');
const fs = BBPromise.promisifyAll(require('fs'));
const {join, normalize, sep} = require('path');
const {
getListing,
isMainPageFromUrl,
formatBasepath,
} = require('./util/listing');
const {handleApiRequest} = require('./api/api');
const {join} = require('path');
const {renderTemplate} = require('./template');

const pc = process;
Expand All @@ -30,34 +35,6 @@ const mainComponent = join(__dirname, '/components/main.js');
// CSS
const mainCssFile = join(__dirname, '/main.css');


function isMaliciousPath(path, rootPath) {
return (path + sep).substr(0, rootPath.length) !== rootPath;
}


async function getListing(rootPath, basepath) {
const path = normalize(join(rootPath, basepath));

if (~path.indexOf('\0')) {
return null;
}

if (isMaliciousPath(path, rootPath)) {
return null;
}

try {
if ((await fs.statAsync(path)).isDirectory()) {
return fs.readdirAsync(path);
}
} catch (unusedE) {
/* empty catch for fallbacks */
return null;
}
}


let shouldCache = true;
function setCacheStatus(cacheStatus) {
shouldCache = cacheStatus;
Expand All @@ -76,22 +53,6 @@ async function bundleMain() {
return bundle;
}


function isMainPageFromUrl(url) {
return url == '/';
}


/**
* Adds a trailing slash if missing.
* @param {string} basepath
* @return {string}
*/
function formatBasepath(basepath) {
return basepath.replace(/[^\/]$/, lastChar => `${lastChar}/`);
}


function serveIndex({root, mapBasepath}) {
const mapBasepathOrPassthru = mapBasepath || (url => url);

Expand All @@ -103,6 +64,12 @@ function serveIndex({root, mapBasepath}) {
}

return (async() => {

if (req.path.startsWith('/dashboard/api')) {
handleApiRequest(root, req, res, next);
return;
}

const isMainPage = isMainPageFromUrl(req.url);
const basepath = mapBasepathOrPassthru(req.url);

Expand Down
101 changes: 82 additions & 19 deletions build-system/app-index/main.css
Expand Up @@ -23,6 +23,10 @@ body {
body.scroll-locked {
overflow: hidden;
}
/* Hide the three default dots */
.custom-loader .amp-active > div {
display: none;
}
.amp-logo {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='175' height='60' viewBox='0 0 175 60'%3E%3Ctitle%3EAMP-Brand-Blue%3C/title%3E%3Cg fill='%230379C4' fill-rule='evenodd'%3E%3Cpath d='M92.938 34.265h7.07l-2.38-7.015c-.153-.445-.334-.97-.54-1.574-.21-.603-.414-1.256-.615-1.96-.188.715-.384 1.378-.585 1.988-.202.61-.392 1.136-.57 1.58l-2.38 6.98zm16.632 9.794h-4.656c-.522 0-.95-.122-1.29-.362-.336-.24-.57-.548-.7-.923l-1.528-4.465h-9.844l-1.53 4.465c-.116.328-.348.625-.69.888-.344.264-.765.396-1.263.396h-4.692L93.4 18.44h6.147L109.57 44.06zM130.562 33.736c.22.48.43.975.63 1.48.202-.518.415-1.02.64-1.505.225-.487.456-.96.693-1.416l6.645-12.954c.12-.224.24-.397.365-.52.124-.123.264-.214.417-.273.154-.06.33-.088.524-.088h5.27v25.6h-5.295V29.324c0-.714.035-1.49.106-2.32l-6.858 13.168c-.213.41-.5.72-.862.932-.362.21-.773.316-1.235.316h-.816c-.462 0-.873-.104-1.235-.315-.363-.21-.65-.52-.864-.932l-6.893-13.187c.047.41.083.818.106 1.222.023.405.035.778.035 1.117V44.06h-5.295v-25.6h5.269c.194 0 .37.028.523.087.154.06.293.15.417.274.125.123.246.296.365.52l6.663 13.006c.237.446.464.91.684 1.39M160.99 31.013h3.127c1.563 0 2.688-.37 3.376-1.108.687-.738 1.03-1.77 1.03-3.094 0-.585-.088-1.12-.266-1.6-.178-.48-.448-.893-.808-1.24-.363-.344-.82-.612-1.37-.8-.55-.187-1.206-.28-1.963-.28h-3.127v8.123zm0 4.483v8.562h-6.006V18.442h9.133c1.824 0 3.39.214 4.7.64 1.308.43 2.387 1.018 3.233 1.77.847.75 1.473 1.634 1.875 2.653.403 1.02.604 2.122.604 3.306 0 1.278-.208 2.45-.622 3.518-.416 1.065-1.05 1.98-1.902 2.742-.853.762-1.934 1.357-3.243 1.784-1.308.43-2.857.642-4.646.642h-3.127zM40.674 27.253L27.968 48.196h-2.302l2.276-13.647-7.048.01h-.1c-.633 0-1.148-.51-1.148-1.137 0-.27.254-.727.254-.727l12.664-20.92 2.34.01-2.332 13.668 7.084-.008.112-.002c.635 0 1.15.51 1.15 1.14 0 .254-.1.478-.245.668zM30.288 0C13.56 0 0 13.432 0 30 0 46.57 13.56 60 30.288 60c16.73 0 30.29-13.431 30.29-30 0-16.568-13.56-30-30.29-30z'/%3E%3C/g%3E%3C/svg%3E");
width: 87px;
Expand Down Expand Up @@ -160,7 +164,7 @@ a.find-icon:hover {
}
code,
.code,
.file-list > li > a {
.file-list .file-link, .file-list-search {
font-family: 'Fira Code', 'Inconsolata', Menlo, Consolas, monospace;
}
a {
Expand All @@ -184,12 +188,7 @@ a.underlined::before {
position: absolute;
right: 0;
}
.push-right-after-heading {
display: flex;
float: right;
margin-top: -70px;
line-height: 20px;
}

#examples-mode-select {
margin: 0 20px 0 10px;
}
Expand All @@ -208,25 +207,90 @@ a.underlined::before {
}
.text-input:focus {
border-color: #0389ff;
border-bottom-width: 2px;
margin-bottom: -7px;
outline: none;
}

.file-list-heading {
display: flex;
flex-wrap: wrap;
justify-content: start;
align-items: center;

padding-top: 30px;
padding-bottom: 30px;
}

.file-list-heading #basepath {
padding: 0;
}

.file-list-right-section {
margin-left: auto;
}

.file-list-search {
flex: 1;
font-size: 18px;
padding: 4px 0;
font-weight: normal;
border: none;
border-bottom: solid #ddd 1px;
background: transparent;
margin: 0 20px 0 8px;
}

.file-list-search:focus {
outline: none;
border-color: #0389ff;
border-bottom-width: 2px;
margin-bottom: -1px;
}

.file-list-search::placeholder {
font-weight: normal;
}

.file-list {
padding: 0 0 40px;
}

.file-list div[role="list"] {
margin: 0;
column-count: 3;
column-gap: 20px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: flex-start;

min-height: initial;
}

.file-list .list-overflow[overflow] {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}

.file-list .file-link-container {
margin-bottom: 10px;
margin-right: 20px;
height: 20px;
}
.file-list > li {

.file-list .file-link {
padding: 0;
margin: 0 0 10px;
margin: 0;
display: inline-block;
width: 300px;
display: block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.file-list > li > a:hover {

.file-list .file-link:hover {
text-decoration: underline;
color: #333;
}
Expand Down Expand Up @@ -260,9 +324,11 @@ header li.divider {
padding-right: 20px;
border-right: 1px solid #ddd;
}
@media (max-width: 1000px) {
.file-list {
column-count: 2;
@media (max-width: 900px) {
.file-list-right-section {
width: 100%;
margin-top: 15px;
margin-bottom: 0px;
}
}
@media (max-width: 580px) {
Expand All @@ -272,10 +338,7 @@ header li.divider {
flex: 0 0 100%;
margin-bottom: 20px;
}
.file-list {
column-count: 1;
}
.file-list > li {
.file-list .file-link {
display: block;
width: 100%;
}
Expand Down

0 comments on commit 96d8931

Please sign in to comment.