Slides at: https://jrjohnson.dev/talks/2017-01-apifirst-microapps.html
jrjohnson
jrjohnson_
jrjohnson https://symfony-devs.slack.com/
Credit: Daniel M. Harris et al, American Physical Society
/** * Written because slice() is dysfunctional with associative arrays. * * @return a deep copy of the associative array (the returned array is its own unique * Array instance, and the objects in the array have clone() called on them - * so they must implement that). If originalArray is null, null is returned. */ilios.utilities.deepCloneAssociativeArray = function (originalArray) { var rhett = null; if (originalArray != null) { rhett = new Array(); for (var key in originalArray) { rhett[key] = originalArray[key].clone(); rhett.length++; } } return rhett;};
A single-page application (SPA) is a web application or web site that fits on a single web page with the goal of providing a more fluid user experience akin to a desktop application. In a SPA, either all necessary code – HTML, JavaScript, and CSS – is retrieved with a single page load,[1] or the appropriate resources are dynamically loaded and added to the page as necessary, usually in response to user actions. The page does not reload at any point in the process, nor does control transfer to another page, although modern web technologies (such as those included in the HTML5 pushState() API) can provide the perception and navigability of separate logical pages in the application. Interaction with the single page application often involves dynamic communication with the web server behind the scenes.
https://en.wikipedia.org/wiki/Single-page_application
... all necessary code – HTML, JavaScript, and CSS – is retrieved with a single page load ... The page does not reload at any point ... dynamic communication with the web server behind the scenes.
... all necessary code – HTML, JavaScript, and CSS – is retrieved with a single page load ... The page does not reload at any point ... dynamic communication with the web server behind the scenes.
REST or RESTfull APIs deliver stateless data based on URL and allow for authorized CRUD operations on that data.
REST or RESTfull APIs deliver stateless data based on URL and allow for authorized CRUD operations on that data.
--Jon Johnson, SFPHP Meetup, Just Now
Shibboleth LDAP Form Based Active Directory
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpbGlvcyIsImF1ZCI6ImlsaW9zIiwiaWF0IjoiMTQ0NjA2MDU4NCIsImV4cCI6IjE0NDYwODkzODQiLCJ1c2VyX2lkIjoyMDAwfQ.khdJqxEbXZwudOjjUr8_fs4BJmespfb8qAx2v_zIleU
{ "iss": "ilios", "aud": "ilios", "iat": "1446060584", "exp": "1446089384", "user_id": 2000}
{ "iss": "ilios", "aud": "ilios", "iat": "1446060584", "exp": "1446089384",* "user_id": 2000,* "roles": ['user', 'admin'],* "favoriteTypeOfBacon": 'smoked applewood'}
With the user authenticated we can now fetch some data
With the user authorized we can now display data
public function getAction($id){ $manager = $this->get('course.manager'); $course = $manager->findBy(['id' => $id]); $authChecker = $this->get('security.authorization_checker'); if (! $authChecker->isGranted('view', $course)) { throw $this->createAccessDeniedException('Unauthorized access!'); } $answer['courses'][] = $course; return $answer;}
/api/v1/courses/619
"courses":[ { "id":619, "title": "PRIME 2014-15", "startDate": "2014-08-23T00:00:00+00:00", "deleted": false, "school": "1", "directors": ["3625", "3906"], "topics": [], "objectives": [], "sessions": ["17713", "17714", "17715"] } ]
Our Course Entity
class Course{ /** * @Type("string") */ public $title;
composer require jms/serializer
public function testGetCourse(){ $course = $this->container->get('dataloader.course'); $this->createJsonRequest('GET', '/api/v1/courses/' . $course['id']); $response = $this->client->getResponse(); $this->assertEquals( $course, json_decode($response->getContent(), true)['courses'][0] );}
public function testPostBadCourse(){ $invalidCourse = $this->container ->get('dataloader.course') ->createInvalid() ; $this->createJsonRequest( 'POST', $this->getUrl('post_courses'), json_encode(['course' => $invalidCourse]), $this->getAuthenticatedUserToken() ); $response = $this->client->getResponse(); $this->assertEquals(Codes::HTTP_BAD_REQUEST, $response->getStatusCode());}
public function testFilterByLevel(){ $courses = $this->container->get('dataloader.course')->getAll(); $this->createJsonRequest( 'GET', $this->getUrl('cget_courses', ['filters[level]' => 3]), null, $this->getAuthenticatedUserToken() ); $response = $this->client->getResponse(); $this->assertJsonResponse($response, Codes::HTTP_OK); $data = json_decode($response->getContent(), true)['courses']; $this->assertEquals(1, count($data), var_export($data, true)); $this->assertEquals( $this->mockSerialize( $courses[3] ), $data[0] );}
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Ilios</title> <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"><meta name="ilios/config/environment" content="%7B%22modulePrefix%22%3A%22ilios%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22/%22%2C%22locationType%22%3A%22auto%22%2C%22redirectAfterShibLogin%22%3Atrue%2C%22contentSecurityPolicy%22%3A%7B%22default-src%22%3A%5B%22%27none%27%22%5D%2C%22script-src%22%3A%5B%22%27self%27%22%2C%22%27unsafe-eval%27%22%2C%22www.google-analytics.com%22%5D%2C%22font-src%22%3A%5B%22%27self%27%22%5D%2C%22connect-src%22%3A%5B%22%27self%27%22%2C%22www.google-analytics.com%22%2C%22http%3A//localhost%3A8000%22%5D%2C%22img-src%22%3A%5B%22%27self%27%22%2C%22data%3A%22%2C%22www.google-analytics.com%22%5D%2C%22style-src%22%3A%5B%22%27self%27%22%2C%22%27unsafe-inline%27%22%5D%2C%22media-src%22%3A%5B%22%27self%27%22%5D%7D%2C%22flashMessageDefaults%22%3A%7B%22timeout%22%3A2000%2C%22extendedTimeout%22%3A3000%2C%22types%22%3A%5B%22success%22%2C%22warning%22%2C%22info%22%2C%22alert%22%5D%7D%2C%22ember-simple-auth%22%3A%7B%22authorizer%22%3A%22authorizer%3Atoken%22%7D%2C%22ember-simple-auth-token%22%3A%7B%22serverTokenEndpoint%22%3A%22/auth/login%22%2C%22serverTokenRefreshEndpoint%22%3A%22/auth/token%22%2C%22tokenPropertyName%22%3A%22jwt%22%2C%22authorizationHeaderName%22%3A%22X-JWT-Authorization%22%2C%22authorizationPrefix%22%3A%22Token%20%22%2C%22refreshLeeway%22%3A300%7D%2C%22i18n%22%3A%7B%22defaultLocale%22%3A%22en%22%7D%2C%22froalaEditorDefaults%22%3A%7B%22toolbarInline%22%3Afalse%2C%22placeholderText%22%3A%22%22%2C%22allowHTML%22%3Atrue%2C%22saveInterval%22%3Afalse%2C%22pastePlain%22%3Atrue%2C%22spellcheck%22%3Atrue%2C%22toolbarButtons%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%2C%22toolbarButtonsMD%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%2C%22toolbarButtonsSM%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%2C%22toolbarButtonsXS%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%7D%2C%22serverVariables%22%3A%7B%22tagPrefix%22%3A%22iliosconfig%22%2C%22vars%22%3A%5B%22api-host%22%2C%22api-name-space%22%5D%2C%22defaults%22%3A%7B%22api-name-space%22%3A%22api/v1%22%2C%22api-host%22%3A%22http%3A//localhost%3A8000%22%7D%7D%2C%22ember-metrics%22%3A%7B%22includeAdapters%22%3A%5B%22google-analytics%22%5D%7D%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22String%22%3Atrue%2C%22Array%22%3Atrue%2C%22Function%22%3Afalse%2C%22Date%22%3Afalse%7D%7D%2C%22APP%22%3A%7B%22apiVersion%22%3A%22v1.12%22%2C%22name%22%3A%22ilios%22%2C%22version%22%3A%221.30.2+c4cf1099%22%7D%2C%22IliosFeatures%22%3A%7B%22allowAddNewUser%22%3Atrue%7D%2C%22contentSecurityPolicyHeader%22%3A%22Content-Security-Policy-Report-Only%22%2C%22something%22%3A%22test%22%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22noScript%22%3A%7B%22tag%22%3A%22noscript%22%2C%22content%22%3A%22%3Cp%3EFor%20full%20functionality%20of%20this%20site%20it%20is%20necessary%20to%20enable%20JavaScript.Here%20are%20the%20%3Ca%20href%3D%27http%3A//www.enable-javascript.com/%27%20target%3D%27_blank%27%3Einstructions%20how%20to%20enable%20JavaScript%20in%20your%20web%20browser%3C/a%3E.%3C/p%3E%22%7D%7D" /> <link rel="stylesheet" href="https://d26vzvixg52o0d.cloudfront.net/assets/vendor-f5d338174298b781680826893d072095.css"> <link rel="stylesheet" href="https://d26vzvixg52o0d.cloudfront.net/assets/ilios-555dc260d918b4f9b035ec1db2c35944.css"> <!-- Ilios variables set by the web server --><meta name='iliosconfig-api-name-space'content='api/v1'><meta name='iliosconfig-api-host'content='http://localhost:8000'> </head> <body> <noscript><p>For full functionality of this site it is necessary to enable JavaScript.Here are the <a href='http://www.enable-javascript.com/' target='_blank'>instructions how to enable JavaScript in your web browser</a>.</p></noscript><div id='initialpageloader' class='ember-load-indicator'><header class='main'><div class='logo'><span class='image'></span></div></header><div id='site-container'><h1><i class='fa fa-spinner fa-pulse fa-3x'></i></h1><p id='browsererrormessage' class='hidden'>It is possible that your browser is not supported by Ilios. Please refresh this page or try a different browser.</p></div></div></div> <script src="https://d26vzvixg52o0d.cloudfront.net/assets/vendor-176c84d4f1739a5cbd0342f2dae4984e.js"></script> <script src="https://d26vzvixg52o0d.cloudfront.net/assets/ilios-008c1daa7f9adbaf79122bce1c44a4b2.js"></script> <script src="https://d26vzvixg52o0d.cloudfront.net/ilios-prerender/scripts-0184e9b3a1818f3c046d9d735319dd44.js"></script> </body></html>
<meta name="ilios/config/environment" content="%7B%22modulePrefix%22%3A%22ilios%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22/%22%2C%22locationType%22%3A%22auto%22%2C%22redirectAfterShibLogin%22%3Atrue%2C%22contentSecurityPolicy%22%3A%7B%22default-src%22%3A%5B%22%27none%27%22%5D%2C%22script-src%22%3A%5B%22%27self%27%22%2C%22%27unsafe-eval%27%22%2C%22www.google-analytics.com%22%5D%2C%22font-src%22%3A%5B%22%27self%27%22%5D%2C%22connect-src%22%3A%5B%22%27self%27%22%2C%22" />
modulePrefix: 'ilios', environment: environment, rootURL: '/', locationType: 'auto', redirectAfterShibLogin: true, contentSecurityPolicy: { 'default-src': ["'none'"], 'script-src': ["'self'", "'unsafe-eval'", 'www.google-analytics.com'], 'font-src': ["'self'"], 'connect-src': ["'self'", 'www.google-analytics.com'], 'img-src': ["'self'", 'data:', 'www.google-analytics.com'], 'style-src': ["'self'", "'unsafe-inline'"], 'media-src': ["'self'"] }, ...
<meta name='iliosconfig-api-name-space' content='api/v1'><meta name='iliosconfig-api-host' content='http://localhost:8000'>
<link rel="stylesheet" href="https://d26vzvixg52o0d.cloudfront.net/assets/vendor-f5d338174298b781680826893d072095.css"> <link rel="stylesheet" href="https://d26vzvixg52o0d.cloudfront.net/assets/ilios-555dc260d918b4f9b035ec1db2c35944.css">
<script src="https://d26vzvixg52o0d.cloudfront.net/assets/vendor-176c84d4f1739a5cbd0342f2dae4984e.js"></script><script src="https://d26vzvixg52o0d.cloudfront.net/assets/ilios-008c1daa7f9adbaf79122bce1c44a4b2.js"></script>
<noscript> <p> For full functionality of this site it is necessary to enable JavaScript. Here are the <a href='http://www.enable-javascript.com/' target='_blank'>instructions on how to enable JavaScript in your web browser</a>. </p> </noscript>
<div class='load-indicator'> <h1><i class='fa fa-spinner fa-pulse fa-3x'></i></h1> <p class='browsererrormessage hidden'> It is possible that your browser is not supported by Ilios. Please refresh this page or try a different browser. </p></div><script src="https://d26vzvixg52o0d.cloudfront.net/ilios-prerender/scripts-0184e9b3a1818f3c046d9d735319dd44.js"></script>
"meta": [ { "name": "ilios/config/environment", "content": "%7B%22modulePrefix%22%3A%22ilios%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22/%22%2C%22locationType%22%3A%22auto%22%2C%22redirectAfterShibLogin%22%3Atrue%2C%22contentSecurityPolicy%22%3A%7B%22default-src%22%3A%5B%22%27none%27%22%5D%2C%22script-src%22%3A%5B%22%27self%27%22%2C%22%27unsafe-eval%27%22%2C%22www.google-analytics.com%22%5D%2C%22font-src%22%3A%5B%22%27self%27%22%5D%2C%22connect-src%22%3A%5B%22%27self%27%22%2C%22www.google-analytics.com%22%2Cnull%5D%2C%22img-src%22%3A%5B%22%27self%27%22%2C%22data%3A%22%2C%22www.google-analytics.com%22%5D%2C%22style-src%22%3A%5B%22%27self%27%22%2C%22%27unsafe-inline%27%22%5D%2C%22media-src%22%3A%5B%22%27self%27%22%5D%7D%2C%22flashMessageDefaults%22%3A%7B%22timeout%22%3A2000%2C%22extendedTimeout%22%3A3000%2C%22types%22%3A%5B%22success%22%2C%22warning%22%2C%22info%22%2C%22alert%22%5D%7D%2C%22ember-simple-auth%22%3A%7B%22authorizer%22%3A%22authorizer%3Atoken%22%7D%2C%22ember-simple-auth-token%22%3A%7B%22serverTokenEndpoint%22%3A%22/auth/login%22%2C%22serverTokenRefreshEndpoint%22%3A%22/auth/token%22%2C%22tokenPropertyName%22%3A%22jwt%22%2C%22authorizationHeaderName%22%3A%22X-JWT-Authorization%22%2C%22authorizationPrefix%22%3A%22Token%20%22%2C%22timeFactor%22%3A1000%7D%2C%22i18n%22%3A%7B%22defaultLocale%22%3A%22en%22%7D%2C%22froalaEditorDefaults%22%3A%7B%22toolbarInline%22%3Afalse%2C%22placeholderText%22%3A%22%22%2C%22allowHTML%22%3Atrue%2C%22saveInterval%22%3Afalse%2C%22pastePlain%22%3Atrue%2C%22spellcheck%22%3Atrue%2C%22toolbarButtons%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%2C%22toolbarButtonsMD%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%2C%22toolbarButtonsSM%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%2C%22toolbarButtonsXS%22%3A%5B%22bold%22%2C%22italic%22%2C%22subscript%22%2C%22superscript%22%2C%22formatOL%22%2C%22formatUL%22%2C%22insertLink%22%2C%22html%22%5D%7D%2C%22serverVariables%22%3A%7B%22tagPrefix%22%3A%22iliosconfig%22%2C%22vars%22%3A%5B%22api-host%22%2C%22api-name-space%22%5D%2C%22defaults%22%3A%7B%22api-name-space%22%3A%22api/v1%22%2C%22api-host%22%3Anull%7D%7D%2C%22ember-metrics%22%3A%7B%22includeAdapters%22%3A%5B%22google-analytics%22%5D%7D%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22String%22%3Atrue%2C%22Array%22%3Atrue%2C%22Function%22%3Afalse%2C%22Date%22%3Afalse%7D%7D%2C%22APP%22%3A%7B%22apiVersion%22%3A%22v1.12%22%2C%22name%22%3A%22ilios%22%2C%22version%22%3A%221.30.2+cc2625f3%22%7D%2C%22IliosFeatures%22%3A%7B%22allowAddNewUser%22%3Atrue%7D%2C%22contentSecurityPolicyHeader%22%3A%22Content-Security-Policy-Report-Only%22%2C%22something%22%3A%22test%22%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22noScript%22%3A%7B%22tag%22%3A%22noscript%22%2C%22content%22%3A%22%3Cp%3EFor%20full%20functionality%20of%20this%20site%20it%20is%20necessary%20to%20enable%20JavaScript.Here%20are%20the%20%3Ca%20href%3D%27http%3A//www.enable-javascript.com/%27%20target%3D%27_blank%27%3Einstructions%20how%20to%20enable%20JavaScript%20in%20your%20web%20browser%3C/a%3E.%3C/p%3E%22%7D%7D" }],"link": [ { "rel": "stylesheet", "href": "https://d26vzvixg52o0d.cloudfront.net/assets/vendor-f5d338174298b781680826893d072095.css" }, { "rel": "stylesheet", "href": "https://d26vzvixg52o0d.cloudfront.net/assets/ilios-555dc260d918b4f9b035ec1db2c35944.css" }],"script": [ { "src": "https://d26vzvixg52o0d.cloudfront.net/assets/vendor-b2e879b4494e5f1bb5108d090e9a684b.js" }, { "src": "https://d26vzvixg52o0d.cloudfront.net/assets/ilios-8ff96141a6193e852624d3b5ee4114d1.js" }, { "src": "https://d26vzvixg52o0d.cloudfront.net/ilios-prerender/scripts-0184e9b3a1818f3c046d9d735319dd44.js" }]
Slides at: https://jrjohnson.dev/talks/2017-01-apifirst-microapps.html
jrjohnson
jrjohnson_
jrjohnson https://symfony-devs.slack.com/
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |