Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7b4d159
fix me later! PROD-278: Write test for fetching the results from Bing…
Sep 14, 2016
5f7def4
fix me later! PROD-278: Write backend test to get the data from the b…
Sep 14, 2016
c83d2d9
fix me later! PROD-278: Write code to make request to bing api in the…
Sep 14, 2016
3fd12a1
fix me later! PROD-278: Use urllib2 instead of requests because it is…
Sep 14, 2016
ed3abd3
fix me later! PROD-278: Try adding User-Agent header but no luck
Sep 14, 2016
a871a85
fix me later! PROD-278: fake commit for Frank to debug it.
Sep 14, 2016
be6daa8
Got it working by adding the get params to the link itself.
Sep 14, 2016
df9040e
fix me later! PROD-278: done with setting up the structure of the res…
Sep 14, 2016
d932dbb
fix me later! PROD-278: Trying to fix the bug when trying to make a r…
Sep 14, 2016
d660ba1
fix me later! PROD-278: Write the code to show the data in the frontend.
Sep 14, 2016
f2a1adc
PROD-278: Fix the bug where no results from bing threw an exception.
Sep 14, 2016
1087297
fix me later! PROD-278: Return all the results from bing (which is 10…
Sep 15, 2016
43a56b0
fix me later! PROD-278: Got the results to be displayed in the front …
Sep 15, 2016
97aa1b5
fix me later! PROD-278: Return displayUrl as a part of response from …
Sep 15, 2016
965c67b
fix me later! PROD-278: Return the snippet(description) of the websit…
Sep 15, 2016
ab0ae5d
fix me later! PROD-278: Create a service to handle the hiding of the …
Sep 15, 2016
18b4465
fix me later! PROD-278: Add feature to hide the reports when making a…
Sep 15, 2016
b03983d
PROD-278: Add a 'google instead' button if the user decides to google…
Sep 15, 2016
29a946d
fix me later! PROD-278: Not working. Trying to make the searches from…
Sep 15, 2016
64ea117
PROD-278: Fix the failing tests because of the changes introduced in …
Sep 15, 2016
4b3e352
fix me later! PROD-278: Add new result component to hold the results …
Sep 15, 2016
268ed7e
fix me later! PROD-278: Finally made it to display the results using …
Sep 15, 2016
f87793b
fix me later! PROD-278: Separate the 'search' and 'google instead?' b…
Sep 15, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions backend/handlers/api/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import datetime
import logging
import urllib
import json
from google.appengine.api import urlfetch

from flask import request, abort, jsonify
from shared import app
Expand Down Expand Up @@ -29,8 +31,22 @@ def query_handler():
# If we get an unauthenticated query request, just go ahead and redirect them to google for now
# without recording it in the database. (We may change this behavior soon)
if not security.is_request_with_auth(request):
return jsonify(ApiResponse({'redirect': create_google_redirect(query_string)}))

data = get_bing_data(query_string)
if 'webPages' in data:
data = data['webPages']['value']
else:
return jsonify(ApiResponse())
payload = []
for i in data:
data = {
'name': i['name'],
'url': i['url'],
'displayUrl': i['displayUrl'],
'snippet': i['snippet']
}
payload.append(data)
payload.append({'googleRedirectLink': create_google_redirect(query_string)})
return jsonify(ApiResponse(payload))
# The request WAS trying to authenticate, so let's try to get the authenticated user.
try:
user = security.authenticate_user(request)
Expand Down Expand Up @@ -65,7 +81,22 @@ def query_handler():
# Save to the datatore.
query.put()
logging.debug('query: %s', str(query))
return jsonify(ApiResponse({'redirect': create_google_redirect(query_string)}))
data = get_bing_data(query_string)
if 'webPages' in data:
data = data['webPages']['value']
else:
return jsonify(ApiResponse())
payload = []
for i in data:
data = {
'name': i['name'],
'url': i['url'],
'displayUrl': i['displayUrl'],
'snippet': i['snippet']
}
payload.append(data)
payload.append({'googleRedirectLink': create_google_redirect(query_string)})
return jsonify(ApiResponse(payload))


def get_tags(search_string):
Expand Down Expand Up @@ -107,3 +138,18 @@ def create_google_redirect(search_string):
escaped_q = urllib.urlencode({'q': search_string})
redirect = 'https://google.com/#' + escaped_q
return redirect


def get_bing_data(query_string):
""" Get the search result using Bing's api. """
url = 'https://api.cognitive.microsoft.com/bing/v5.0/search?'+urllib.urlencode({'q':query_string})

try:
headers = {'Ocp-Apim-Subscription-Key': 'd4ded470d517472da9b40836ab319538'}
result = urlfetch.fetch(
url=url,
method=urlfetch.GET,
headers=headers)
except urlfetch.Error:
logging.exception('Caught exception fetching url')
return json.loads(result.content)
5 changes: 5 additions & 0 deletions backend/tests/handlers_api_query_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ def test_create_google_redirect(self):
redirect = query.create_google_redirect("&? Search test")
self.assertEqual(redirect, "https://google.com/#q=%26%3F+Search+test")

def test_get_bing_data(self):
""" Test to get the data back from bing. """
data = query.get_bing_data("Is google better than bing ?")
self.assertEqual(data, "No way")

def open_with_auth(self, url, method):
fake_token = get_fake_jwt_token()
return self.app.open(url, method=method, headers={"Authorization": "Bearer " + fake_token},
Expand Down
1 change: 1 addition & 0 deletions default/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ <h1>Stackbot</h1>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-5 col-lg-offset-3">
<result></result>
<auth-integrations></auth-integrations>
<report></report>
</div>
Expand Down
6 changes: 6 additions & 0 deletions default/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@ import { AUTH_PROVIDERS } from 'angular2-jwt';
import { FocusMeDirective } from './shared/focus-me.directive';
import { BackendService } from './shared/backend.service';
import { KeysPipe } from './shared/keys.pipe';
import {ToggleReportService} from './shared/toggle.report.service';


import { AppComponent } from './app.component';
import { ReportComponent } from './report/index';
import { AuthService, AuthButtonComponent, AuthIntegrationsComponent } from './auth/index';
import { SearchComponent } from './search/index';
import { ResultComponent } from './result/index';

// DO NOT DELETE: This is needed or the compiler says, Cannot find namespace 'firebase'.
/* tslint:disable */
import * as firebase from 'firebase';
import {ResultService} from './result/result.service';
/* tslint:enable */

const firebaseConfig = {
Expand All @@ -42,6 +45,8 @@ let providers = [
HTTP_PROVIDERS,
AuthService,
BackendService,
ToggleReportService,
ResultService
];

let declarations = [
Expand All @@ -52,6 +57,7 @@ let declarations = [
FocusMeDirective,
AuthIntegrationsComponent,
KeysPipe,
ResultComponent,
];

let imports = [
Expand Down
2 changes: 1 addition & 1 deletion default/src/app/report/report.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div *ngIf="data.length != 0">
<div *ngIf="data.length != 0 && toggleReport.showReport">
<table class="table">
<thead>
</thead>
Expand Down
4 changes: 3 additions & 1 deletion default/src/app/report/report.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Observable, BehaviorSubject, Observer } from 'rxjs';
import { User } from '../shared/user';
// Note that this also imports moment itself.
import * as moment from 'moment-timezone';
import {ToggleReportService} from '../shared/toggle.report.service';

class MockQueryService {

Expand Down Expand Up @@ -46,7 +47,8 @@ describe('SearchComponent', () => {
addProviders([
ReportComponent,
{provide: QueryService, useClass: MockQueryService},
{provide: AuthService, useClass: MockAuthService}
{provide: AuthService, useClass: MockAuthService},
{provide: ToggleReportService, useClass: ToggleReportService}
]);
});
it('should not call QueryService.getQueries() when user is logged out.',
Expand Down
4 changes: 2 additions & 2 deletions default/src/app/report/report.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { QueryService } from '../query/query.service';
import { AuthService } from '../auth/index';
// Note that this also imports moment itself.
import * as moment from 'moment-timezone';
import {ToggleReportService} from '../shared/toggle.report.service';

@Component({
selector: 'report',
Expand Down Expand Up @@ -46,9 +47,8 @@ export class ReportComponent {
moment.tz.setDefault(this.tz);
}

constructor(private queryService: QueryService, private auth: AuthService) {
constructor(private queryService: QueryService, private auth: AuthService, private toggleReport: ToggleReportService) {
this.setTimezone();

this.auth.getUser().subscribe(
user => {
if (user && user.loggedIn) {
Expand Down
1 change: 1 addition & 0 deletions default/src/app/result/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './result.component';
8 changes: 8 additions & 0 deletions default/src/app/result/result.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<br>
<div *ngIf="resultService.data.length != 0">
<span *ngFor="let link of resultService.data">
<a href="{{link.url}}">{{link.name}}</a><br>
{{link.displayUrl}} <br>
{{link.snippet}} <br><br>
</span>
</div>
13 changes: 13 additions & 0 deletions default/src/app/result/result.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component, Input } from '@angular/core';
import {ResultService} from './result.service';

@Component({
selector: 'result',
templateUrl: 'result.component.html',
})

export class ResultComponent {

constructor(private resultService: ResultService) {}

}
21 changes: 21 additions & 0 deletions default/src/app/result/result.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export class ResultService {

data: any = [];

processData(items: any[]) {
let newData: any = [];
for (let item of items) {
if (item['googleRedirectLink']) {
continue;
}
newData.push({
'name' : item['name'],
'url': item['url'],
'displayUrl': item['displayUrl'],
'snippet': item['snippet']
});
}
this.data = newData;
}

}
4 changes: 3 additions & 1 deletion default/src/app/search/search.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ button:hover {
.left-addon input { padding-left: 30px; }
.right-addon input { padding-right: 30px; }


#google-search-button {
margin-left: 2px;
}
4 changes: 3 additions & 1 deletion default/src/app/search/search.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<span class="input-group-btn">
<button class="btn" type="button" (click)="submit(searchField.value)" id="search-button">Search</button>
</span>
<span class="input-group-btn" *ngIf="googleRedirectLink">
<button class="btn" type="button" (click)="_redirect(googleRedirectLink)" id="google-search-button">Google Instead?</button>
</span>
</div>


47 changes: 46 additions & 1 deletion default/src/app/search/search.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { QueryService } from '../query/index';
import { AuthService} from '../auth/auth.service';
import { Observable, BehaviorSubject, Observer } from 'rxjs';
import { User } from '../shared/user';
import { ToggleReportService } from '../shared/toggle.report.service';

class MockQueryService {

Expand Down Expand Up @@ -46,7 +47,8 @@ describe('SearchComponent', () => {
addProviders([
SearchComponent,
{provide: QueryService, useClass: MockQueryService},
{provide: AuthService, useClass: MockAuthService}
{provide: AuthService, useClass: MockAuthService},
{provide: ToggleReportService, useClass: ToggleReportService}
]);
});
it('submit button should NOT send a query if search field is empty',
Expand Down Expand Up @@ -106,4 +108,47 @@ describe('SearchComponent', () => {
})

);

it('should get the Bing search data from the backend',
inject([SearchComponent, QueryService, AuthService], (component: SearchComponent, querySrv: MockQueryService,
auth: MockAuthService) => {
spyOn(querySrv, 'doQuery').and.callFake(() => {
return Observable.create(
(observer: Observer<any>) => {
observer.next({
'success': 'true',
'payload': [
{
'name': 'fake',
'url': 'fake.com',
'snippet': 'this is fake'
},
{
'redirect': 'http://google.com/q#=whatever',
}
],
'cursor': null
});
observer.complete();
}
);
});

spyOn(component, 'processData');
component.doSearch('search text');
expect(component.processData).toHaveBeenCalledWith([
{
'name': 'fake',
'url': 'fake.com',
'snippet': 'this is fake'
},
{
'redirect': 'http://google.com/q#=whatever',
}
]
);
})

);

});
17 changes: 14 additions & 3 deletions default/src/app/search/search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { Component } from '@angular/core';
import { QueryService } from '../query/index';
import {AuthService} from '../auth/auth.service';
import {ToggleReportService} from '../shared/toggle.report.service';
import {ResultService} from '../result/result.service';

@Component({
selector: 'search',
Expand All @@ -12,8 +14,13 @@ import {AuthService} from '../auth/auth.service';
export class SearchComponent {

private preSearchText: any;
constructor(private queryService: QueryService, private auth: AuthService) {
private googleRedirectLink: any;
constructor(private queryService: QueryService, private auth: AuthService,
private toggleReport: ToggleReportService, private resultService: ResultService) {
this.preSearchText = this.populateSearch(window.location.href);
if (this.preSearchText) {
this.submit(this.preSearchText);
}
this.recordOmniSearch(window.location.href);
}

Expand All @@ -33,8 +40,12 @@ export class SearchComponent {
this.queryService.doQuery(searchField, 'site-search').subscribe(
data => {
// If when data is returned from a query with a redirect set, do the redirect.
if (data['payload'] && data['payload']['redirect']) {
this._redirect(data['payload']['redirect']);
if (data['payload'] && data['success']) {
this.resultService.processData(data['payload']);
this.toggleReport.hideReport();
if (data['payload'][data['payload'].length - 1]['googleRedirectLink']) {
this.googleRedirectLink = data['payload'][data['payload'].length - 1]['googleRedirectLink'];
}
}
}
);
Expand Down
9 changes: 9 additions & 0 deletions default/src/app/shared/toggle.report.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class ToggleReportService {

showReport: boolean = true;

hideReport() {
this.showReport = false;
}

}