Skip to content

Instantly share code, notes, and snippets.

@ianbarber
Created May 20, 2014 21:52
Show Gist options
  • Save ianbarber/e71ad6612f0c52589d39 to your computer and use it in GitHub Desktop.
Save ianbarber/e71ad6612f0c52589d39 to your computer and use it in GitHub Desktop.
Silex based API for a tek-in example of PHP on AppEngine
<?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