Created
May 20, 2014 21:52
-
-
Save ianbarber/e71ad6612f0c52589d39 to your computer and use it in GitHub Desktop.
Silex based API for a tek-in example of PHP on AppEngine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once __DIR__.'/../../config.php'; | |
require_once __DIR__.'/../vendor/autoload.php'; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Silex\Application; | |
session_start(); | |
$app = new Application(); | |
$client = buildClient(); | |
// $app['debug'] = true; | |
/** | |
* GET /api/session - returns a list of the sessions known to the system | |
* grouped by the "slot", where all talks on at the same time share a slot. | |
* | |
* Slot is recorded as minutes since the opening keynote. | |
*/ | |
$app->get('/api/session', function (Application $app, Request $request) use ($client) { | |
$memcache = new Memcached(); | |
$memsesslist = $memcache->get('sesslist'); | |
if (!$memsesslist) { | |
$datastore = new Google_Service_Datastore($client); | |
$request = new Google_Service_Datastore_RunQueryRequest(); | |
$query = new Google_Service_Datastore_Query(); | |
$kind = new Google_Service_Datastore_KindExpression(); | |
$kind->setName(SESSION_TYPE); | |
$query->setKinds([$kind]); | |
$prop = new Google_Service_Datastore_PropertyReference(); | |
$prop->setName('starttime'); | |
$order = new Google_Service_Datastore_PropertyOrder(); | |
$order->setProperty($prop); | |
$order->setDirection("asc"); | |
$query->setOrder([$order]); | |
$request->setQuery($query); | |
try { | |
$result = $datastore->datasets->runQuery(DATASET_ID, $request); | |
$prev = array(); | |
foreach($result->getBatch()->getEntityResults() as $entity) { | |
$s = Tek\Session::fromEntity($entity->getEntity()); | |
if (count($prev) && $prev[0]->starttime != $s->starttime) { | |
// We have ended a slot. Add the hallway track. | |
$h = Tek\Session::makeHallway($prev[0]->starttime); | |
$prev[] = $h; | |
$memsesslist[] = array('slot' => $h->starttime, 'sessions' => $prev); | |
$prev = array(); | |
} | |
$prev[] = $s; | |
} | |
// Handle the final slot. | |
if (count($prev)) { | |
$h = Tek\Session::makeHallway($prev[0]->starttime); | |
$prev[] = $h; | |
$memsesslist[] = array('slot' => $h->starttime, 'sessions' => $prev); | |
} | |
} catch(Exception $e) { | |
syslog(LOG_WARNING, print_r($e, true)); | |
return $app->json(array("error" => $e->getMessage()), 500); | |
} | |
$memcache->set('sessions', $memsesslist, 300); // 5 minutes. | |
} | |
$headers = array( | |
'Cache-Control' => 'public, max-age=360' | |
); | |
return $app->json($memsesslist, 200, $headers); | |
}); | |
/** | |
* GET /api/session/<session_id> - returns a list of attendees who have | |
* checked in to that session. Each attendee has a givenname and a photo | |
* URL. | |
*/ | |
$app->get('/api/session/{session_id}', function (Application $app, Request $request, $session_id) use ($client) { | |
$memcache = new Memcached(); | |
$memsess = $memcache->get('session-' . $session_id); | |
if (!$memsess) { | |
$memsess = array(); | |
$datastore = new Google_Service_Datastore($client); | |
$request = new Google_Service_Datastore_RunQueryRequest(); | |
$query = new Google_Service_Datastore_Query(); | |
$kind = new Google_Service_Datastore_KindExpression(); | |
$kind->setName(ATTENDANCE_TYPE); | |
$query->setKinds([$kind]); | |
$filter = new Google_Service_Datastore_Filter(); | |
$session_filter = new Google_Service_Datastore_PropertyFilter(); | |
$session_filter->setOperator("EQUAL"); | |
$prop = new Google_Service_Datastore_PropertyReference(); | |
$prop->setName('sessionid'); | |
$session_filter->setProperty($prop); | |
$value = new Google_Service_Datastore_Value(); | |
$value->setStringValue($session_id); | |
$session_filter->setValue($value); | |
$filter->setPropertyFilter($session_filter); | |
$query->setFilter($filter); | |
$request->setQuery($query); | |
try { | |
$result = $datastore->datasets->runQuery(DATASET_ID, $request); | |
foreach($result->getBatch()->getEntityResults() as $entity) { | |
$q = Tek\Attendance::fromEntity($entity->getEntity()); | |
$memsess[] = $q; | |
} | |
// Add to memcache. | |
$memcache->set('session-' . $session_id, $memsess, 60); // 1 minute. | |
} catch(Exception $e) { | |
syslog(LOG_WARNING, print_r($e, true)); | |
return $app->json(array("error" => $e->getMessage()), 500); | |
} | |
} | |
$headers = array( | |
'Cache-Control' => 'public, max-age=30' | |
); | |
return $app->json($memsess, 200, $headers); | |
}); | |
/** | |
* POST /api/session/<session_id>/attendees - add the current user to the list of attendees. | |
*/ | |
$app->post('/api/session/{session_id}/attendees', function (Application $app, Request $request, $session_id) use ($client) { | |
if (!isset($_SESSION['userid'])) { | |
return $app->json(array('error' => "please authenticate"), 401); | |
} | |
$_SESSION['current_sess'] = $session_id; | |
$memcache = new Memcached(); | |
$userid = $_SESSION['userid']; | |
// Early escape if this is a duplicate checkin. | |
if ($memcache->get("checkin-" . $userid . "-" . $session_id)) { | |
return $app->json(array("result" => "added", "sessionid" => $session_id), 200); | |
} | |
// Find user by user ID. | |
$user = $memcache->get($userid); | |
$datastore = new Google_Service_Datastore($client); | |
if (!$user) { | |
$lookup_req = new Google_Service_Datastore_LookupRequest(); | |
$key = new Google_Service_Datastore_Key(); | |
$path = new Google_Service_Datastore_KeyPathElement(); | |
$path->setKind(USER_TYPE); | |
$path->setName($userid); | |
$key->setPath([$path]); | |
$lookup_req->setKeys([$key]); | |
$result = $datastore->datasets->lookup(DATASET_ID, $lookup_req); | |
$user = Tek\User::fromEntity($result->getFound()[0]->getEntity()); | |
$memcache->set($userid, $user); | |
} | |
// Write to attendees. | |
$attendance = new Tek\Attendance(); | |
$attendance->sessionid = $session_id; | |
$attendance->userid = $user->userid; | |
$attendance->photo = $user->url; | |
$attendance->givenname = $user->givenname; | |
$commit = new Google_Service_Datastore_CommitRequest(); | |
$commit->setMode('NON_TRANSACTIONAL'); | |
$key = new Google_Service_Datastore_Key(); | |
$path = new Google_Service_Datastore_KeyPathElement(); | |
$path->setKind(ATTENDANCE_TYPE); | |
$path->setName($user->userid . "-" . $session_id); | |
$key->setPath([$path]); | |
$entity = new Google_Service_Datastore_Entity(); | |
$entity->setKey($key); | |
$entity->setProperties($attendance->getProperties()); | |
$mutation = new Google_Service_Datastore_Mutation(); | |
$mutation->setUpsert(array($entity)); | |
$commit->setMutation($mutation); | |
try { | |
syslog(LOG_INFO, "Writing attendance to DB"); | |
$result = $datastore->datasets->commit(DATASET_ID, $commit); | |
} catch(Exception $e) { | |
return $app->json(array("error" => $e->getMessage()), 500); | |
} | |
// Cache that this has happened. | |
$memcache->set("checkin-" . $userid . "-" . $session_id, 1); | |
// Clear from cache. | |
$memcache->delete("session-" . $session_id); | |
$headers = array( | |
"Location" => '/api/session/'. $session_id | |
); | |
return $app->json(array("result" => "added", "sessionid" => $session_id), 201, $headers); | |
}); | |
/** | |
* POST /api/user given a code, create or retrieve a user entry and associate it with the current session. | |
*/ | |
$app->post('/api/user', function (Application $app, Request $request) use ($client) { | |
$memcache = new Memcached(); | |
$code = $request->get('code', false); | |
if (!$code) { | |
return new Response("No code", 401); | |
} | |
$client->authenticate($code); | |
$plus = new Google_Service_Plus($client); | |
$me = $plus->people->get('me'); | |
$userid = hash("sha256", $me['id'] + SALT); // some weak obfuscation. | |
$_SESSION['userid'] = $userid; | |
$user = new Tek\User(); | |
$user->userid = $userid; | |
$user->url = $me['image']['url']; | |
$user->givenname = $me['name']['givenName']; | |
$memcache->set($userid, $user); | |
$commit = new Google_Service_Datastore_CommitRequest(); | |
$commit->setMode('NON_TRANSACTIONAL'); | |
$key = new Google_Service_Datastore_Key(); | |
$path = new Google_Service_Datastore_KeyPathElement(); | |
$path->setKind(USER_TYPE); | |
$path->setName($user->userid); | |
$key->setPath([$path]); | |
$entity = new Google_Service_Datastore_Entity(); | |
$entity->setKey($key); | |
$entity->setProperties($user->getProperties()); | |
$mutation = new Google_Service_Datastore_Mutation(); | |
$mutation->setUpsert(array($entity)); | |
$commit->setMutation($mutation); | |
// Get a fresh Google_Client authed by key. | |
$ds_client = buildClient(); | |
$datastore = new Google_Service_Datastore($ds_client); | |
try { | |
syslog(LOG_INFO, "Writing user to DB"); | |
$result = $datastore->datasets->commit(DATASET_ID, $commit); | |
} catch(Exception $e) { | |
return $app->json(array("error" => $e->getMessage()), 500); | |
} | |
$userdata = array( | |
'userid' => $userid, | |
'givenname' => $me['name']['givenName'], | |
); | |
return $app->json($userdata, 200); | |
}); | |
$app->run(); | |
/** | |
* Helper function to construct a configured Google Client | |
*/ | |
function buildClient() { | |
$client = new Google_Client(); | |
$client->setApplicationName("Tek-In"); | |
// Used for the OAuth 2.0 token exchange with sign in. | |
$client->setClientId(CLIENT_ID); | |
$client->setClientSecret(CLIENT_SECRET); | |
$client->setRedirectUri("postmessage"); | |
// Used for Datastore access. | |
$key = file_get_contents(KEY_FILE_PATH); | |
$client->setAssertionCredentials( | |
new Google_Auth_AssertionCredentials( | |
KEY_ACCOUNT_NAME, | |
array(Google_Service_Datastore::DATASTORE, Google_Service_Datastore::USERINFO_EMAIL), | |
$key | |
) | |
); | |
return $client; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment