A single language for backend and frontend from AngularJS to cloud with Claudia.js
-
Upload
corley-srl -
Category
Software
-
view
62 -
download
0
Transcript of A single language for backend and frontend from AngularJS to cloud with Claudia.js
PlanningGET → /book Access to a list of books
GET → /book/:id Access to a single book
POST → /book Create a new book
PATCH → /book/:id Update a given book
DELETE → /book/:id Delete a given book
We can extends this using authentication header Authorization
We have a simple API'use strict' ;
angular.module( 'myApp').service( 'bookService' , function($http) return "list": function() return $http.get( "http://localhost:8085/book" ); , ... // more methods ; );
So we create a serviceOr we can create a provider to configure the URLs etc
With a service we have a boxwith different methods
bookServiceGet all books: list()Get a single book: get(:bookId)Create a book: create(:bookModel)...
The Book model is stable across our applicationtitle: "the book title" , isbn: "12345678" , available: true
Or we resolve the book dependency with the book link (thanks to API uniquiness)
name: "my favourites" , books: [ 1,2,3]
So:$q.all( [1,2,3].map(bookService.get) ).then(function(books) //books is my favourites list with details );
Where the GET'use strict' ;
angular.module( 'myApp').service( 'bookService' , function($http) return "get": function(id) return $http.get( "http://localhost:8085/book/" +id); , "list": function() return $http.get( "http://localhost:8085/book" ); , ... // more methods ; );
A controller can request dataangular.module( 'myApp') .controller( 'MainCtrl' , function ($scope, bookService) $scope.books = []; bookService.list().success(function(books) $scope.books = books; ); );
Thanks to the controller we can pass our books to the view
The scope the communication channel for the data
Add a test case (1/2)describe("MainCtrl" , function() beforeEach(module( 'myApp'));
beforeEach(inject(function ($controller, $rootScope, $httpBackend) scope = $rootScope.$new(); httpBackend = $httpBackend; MainCtrl = $controller( 'MainCtrl' , $scope: scope, // other mocks ); ));
// continue... );
Add a test case (2/2)it('should attach a list of books to the scope' , function () // when someone require the "/book" endpoint reply with a valid response httpBackend.whenGET( "http://localhost:8085/book" ).respond([ id: 1, title: "This is a book" , isbn: "9835623", available: true , id: 2, title: "Another super book" , isbn: "9835624", available: true , id: 3, title: "A rare book to read" , isbn: "9835625", available: false ]); // force the HTTP data resolution httpBackend.flush();
// my expectation are that we books in the controller scope. expect(scope.books.length).toBe( 3); expect(scope.books.map((item) => item.title)).toEqual([ "This is a book" , "Another super book" , "A rare book to read" ]); );
$httpBackend act as a spy for HTTP requests
The directive (web component)angular.module("myApp") .directive( 'booksList' , function() return replace: true, templateUrl: "app/views/bookslist.html" , restrict: "E", scope: "books": "=", , link: function() , ; );
books-list.html<div> <book ngif="book.available" book="book" ngrepeat="book in books" ></book> </div>
The book web componentangular.module("myApp") .directive( 'book', function() return replace: true, templateUrl: "app/views/book.html" , restrict: "E", scope: "book": "=", , link: function() , ; );
Components testing (1/3)describe("Books List" , function() var $compile, $rootScope;
beforeEach(module( 'myApp'));
beforeEach(function() inject(function( _$compile_, _$rootScope _) $compile = _$compile_; $rootScope = _$rootScope _; ); ););
Components testing (2/3)it("should expose our books" , function() $rootScope.books = [ id: 1, title: "This is a book" , isbn: "9835623", available: true id: 2, title: "Another book" , isbn: "9835624", available: true id: 2, title: "A secret book" , isbn: "9835625", available: false ]; var element = $compile( '<bookslist books="books"></bookslist>' )($rootScope);
$rootScope.$digest();
var html = element.html(); expect(html).toContain( "This is a book" ); expect(html).toContain( "Another book" ); expect(html).not.toContain( "A secret book" ); );
Components testing (3/3)Single book directive testing
it("should expose a single book" , function() $rootScope.book = id: 1, title: "This is a single book" , isbn: "9835623", available: true ; var element = $compile( '<book book="book"></book>' )($rootScope);
$rootScope.$digest();
var html = element.html(); expect(html).toContain( "This is a single book" ); );
Add a book creationWe need the create book service methodWe need another dummy web componentWe use the controller to save the book model
The create methodangular.module( 'myApp').service( 'bookService' , function($http) return "create": function(book) return $http.post( "http://localhost:8085/book" , book); , /* already implemented */ "get": function(id) return $http.get( "http://localhost:8085/book/" +id); , "list": function() return $http.get( "http://localhost:8085/book" ); , ; );
Dummy web component (1/3)angular.module("myApp") .directive( 'bookForm' , function() return replace: true, templateUrl: "app/views/bookform.html" , restrict: "E", scope: "book": "=", "save": "=", ; );
We can also skip the book model pass-through
Dummy web component (2/3)<div> <form> Title: < input type="text" ngmodel= "book.title" /><br> ISBN: < input type="text" ngmodel= "book.isbn" /><br> Available: < input type="checkbox" ngmodel= "book.available" /><br> <button type="button" ngclick= "save(book)" >Save</button>< br> </form> </div>
Pay attention on ng-click that pass the book model
Dummy web component (3/3)it("should expose a book", function(done)
$rootScope.book = ;
$rootScope.save = function(book)
expect(book.title).toEqual("Some text");
expect(book.isbn).toEqual("123456");
expect(book.available).toBe(true);
done();
;
var element = $compile('<bookform book="book" save="save"></bookform>')($rootScope);
$rootScope.$digest();
angular.element(element.find('input')[0]).val('Some text').triggerHandler('input');
angular.element(element.find('input')[1]).val('123456').triggerHandler('input');
angular.element(element.find('input')[2]).prop('checked', 'checked').triggerHandler('click');
$rootScope.$apply();
element.find('button')[0].click();
);
So we have validated that the directive calls the save method
But... Who have the save method? Controller + Book Service
Previous Controller (1/2)angular.module( 'myApp') .controller( 'MainCtrl' , function ($scope, bookService) $scope.book = ; $scope.save = function(book) bookService.create(book).success(function(book) $scope.books = $scope.books.concat([book]); ); $scope.book = ; ;
// old parts... $scope.books = []; bookService.list().success(function(books) $scope.books = books; ); );
<bookform book="book" save="save"></bookform>
Previous Controller (2/2)it('should create a new book', function () // something uses the save method var book = title: "This is a book", isbn: "9835623", available: true ; scope.save(book);
// then the backend should works as expected httpBackend.whenGET("http://localhost:8085/book").respond([]); httpBackend.whenPOST("http://localhost:8085/book").respond( Object.assign(, book, id: 1) ); httpBackend.flush();
expect(scope.book).toEqual(); expect(scope.books.length).toBe(1); expect(scope.books[0].id).toBe(1); expect(scope.books[0].title).toEqual("This is a book"); expect(scope.books[0].isbn).toEqual("9835623"); expect(scope.books[0].available).toBe(true); );
ServerLess environmentsThose envs allows us to pay just for every single requests
No requests? No payments! [more or less]
No server maintenance (platform as a service)
Amazon Web ServicesAWS API Gateway []AWS Lambda []AWS DynamoDB []AWS SNS [ ]AWS SQS [ ]AWS CloudWatch [ ]so many services... [ ... ]
API Gateway
Thanks to API Gateway you can create/publish/maintain/monitorand secure APIs at any scale
Essentially: with API Gateway you declare your APIs interfaces and delegate to another component (like Lambda,EC2, etc) the functionality
Amazon DynamoDB is a fully managed NoSQL database service thatprovides fast and predictable performance with seamless scalability.
AWS releases a project: ServerLess thathelps a lot the wiring
https://github.com/serverless/serverless
Claudia expose a very simple interfacevar ApiBuilder = require('claudiaapibuilder' ), api = new ApiBuilder();
api.get('/hello', function (request, response) return "Hello"; );
Similar to express or hapi
You can use return codes and morefeatures...
api.post('/account' , function (request) return api.ApiResponse ( name: "Walter", surname: "Dal Mut" , 201); );
Claudia.js prepare the whole ApiGateway and Lambdaconfiguration (with CORS) automatically for you
claudia create \ name book module \ region euwest 1 \ apimodule book \ config claudiabooks.json
It creates a new module book-module and store in claudia-books.json the Claudia.js conguration
Thanks to Joi we can add data validation to our API
var joi = require(' Joi');var result = joi.object().keys( id: joi.number().required(), title: joi. string().min(1).required() ).validate(request.body);
if (result.error) return new api.ApiResponse (result.error, 406);
// continue
Claudia.js manage JSON data automatically
Claudia.js manage A+ Promises as areturn element
api.post("/sayok", function(request) var d = q.promise();
setTimeout( function() d.resolve( "OK"); , 2000);
return d.promise; );
For Node.js DynamoDB offers a "Document client"
docClient = new AWS.DynamoDB.DocumentClient(region: "euwest1" );
docClient.put( Item: name: "Walter", surname: "Dal Mut", TableName: tableName ).promise();
docClient.get( ...); // primary key docClient.query( ...); // on a secondary index and/or primary key docClient.scan( ...); // search without index (table scan)
All methods have a A+ Promise implementation integrated
Put all togetherapi.post("/book", function(request) var d = q.promise();
var result = joi.object().keys( title: joi. string().min(1).required(), isbn: joi. string().min(1).required(), available: joi.boolean().required() ).validate(request.body);
if (result.error) return new api.ApiResponse (result.error, 406);
var id = uuid.v4(); var item = Object.assign(, id: id, request.body); docClient.put( Item: item, TableName: tableName).promise().then(function() d.resolve(item); );;
return d.promise; );
Of course you can split all responsabilities to dierent components
Thank you for listeningIf you are interested in workshops and/or training sessions feel free
to contact [email protected]
Check out also our page for training at: corsi.corley.it
Check out our corporate website: corley.it