Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jetpack y la WP REST API

Elio
November 05, 2016

Jetpack y la WP REST API

La implementación de la REST API junto con React y Redux en la nueva interface de administración de Jetpack, el plugin de WordPress, y las cosas que se tuvieron en cuenta para ello, como también como se mejora la calidad del código con testing con PHPUnit y GUI testing con Enzyme.

Elio

November 05, 2016
Tweet

More Decks by Elio

Other Decks in Programming

Transcript

  1. WHOAMI const elioRivero = { work: Automattic: { position: ‘Code

    Wrangler’, jetpacker: true }, }, twitter: ‘@eliorivero’ };
  2. CALYPSO ✦ Open Source
 github.com/Automattic/wp-calypso ✦ Implementado con React y

    Flux Redux
 github.com/Automattic/wp-calypso/blob/master/docs/our- approach-to-data.md ✦ REST API propia de WordPress.com
 developer.wordpress.com/docs/api/
  3. JETPACK ✦ Centralizar ajustes ✦ Dar velocidad al usuario ✦

    Implementado con React y Redux ✦ REST API del core de WordPress
 github.com/WP-API/WP-API
  4. REST API ✦ Sólo GET y POST son 99.99999% (?)

    seguros.
 PUT o DELETE devuelven 405 en algunos servidores. ✦ PATHINFO permalinks no funcionan incluso en 4.6.1
 Ahora sí core.trac.wordpress.org/ticket/38182 ✦ Host bloqueaba /wp-json/
  5. REST API ✦ Casting para asegurar que los valores devueltos

    en la response puedan ser correctamente interpretados por JavaScript. ✦ Caché de peticiones remotas a WordPress.com
 (como Post by Email, Related Posts) ✦ Parámetros son enviados desde JS como JSON ✦ Estado inicial para Redux a través de wp_localize_script
  6. REST API ✦ permission_callback
 revisa permisos para procesar la REST

    Request ✦ validate_callback
 verifica que el valor de un parámetro esté entre los admitidos
  7. permission_callback register_rest_route( 'jetpack/v4', '/connection/url', array( 'methods' => WP_REST_Server::READABLE, 'callback' =>

    __CLASS__ . '::build_connect_url', 'permission_callback' => __CLASS__ . '::connect_url_permission_callback', ) ); public static function connect_url_permission_callback() { return current_user_can( 'jetpack_connect_user' ) ? true : new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); }
  8. validate_callback 'jetpack_portfolio_posts_per_page' => array( 'description' => esc_html__( 'Number of entries

    to show at most in Portfolio pages.', 'jetpack' ), 'type' => 'integer', 'default' => 10, 'validate_callback' => __CLASS__ . '::validate_posint', ), public static function validate_posint( $value = 0, $request, $param ) { return is_numeric( $value ) && 0 < $value ? true, : new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a positive integer.', 'jetpack' ), $param ) ); }
  9. TESTING: PHPUNIT ✦ phpunit.de
 Permite detectar errores
 Detecta cuando cambios

    rompen algo ✦ make.wordpress.org/core/handbook/testing/phpunit/ ✦ pippinsplugins.com/unit-tests-wordpress-plugins/
  10. TESTING: PETICIONES REST public function setUp() { parent::setUp(); global $wp_rest_server;

    $this->server = $wp_rest_server = new WP_REST_Server; do_action( 'rest_api_init' ); } public function tearDown() { parent::tearDown(); global $wp_rest_server; $wp_rest_server = null; }
  11. TESTING: PETICIONES REST $request = new WP_REST_Request( 'POST', '/jetpack/v4/connection' );

    $request->set_header( 'content-type', 'application/json' ); $request->set_body( json_encode( array( 'isActive' => false ) ) ); $response = $this->server->dispatch( $request );
  12. REACT: TOOLSET CON NODE.JS webpack Gulp Babel Mocha Chai Enzyme

    Bundler Task runner Transpiler Framework para testing Librería para aserciones Utilidad de testing ✦ ✦ ✦ ✦ ✦ ✦
  13. REDUX: QUERY COMPONENTS ✦ connect()ados ✦ Obtienen datos para la

    store que son disponibles globalmente ✦ Reusables, no visuales ✦ github.com/Automattic/wp-calypso/blob/master/docs/our- approach-to-data.md#query-components
  14. REDUX: LLAMADAS A LA REST API components/connect-button render() { return

    ( <div> <QueryConnectUrl /> { this.renderContent() } </div> ); }
  15. JS: LLAMADAS A LA REST API components/data/query-connect-url export default connect(

    () => { return { fetchConnectUrl: fetchConnectUrl() }; }, ( dispatch ) => { return bindActionCreators( { fetchConnectUrl }, dispatch ); } )( QueryConnectUrl ); fetchConnectUrl: () => fetch( `${ apiRoot }jetpack/v4/connection/ url`, { credentials: 'same-origin', headers: { 'X-WP-Nonce': apiNonce } } ) .then( checkStatus ).then( response => response.json() ),
  16. GUI TESTING ✦ Mocha para correr los tests
 mochajs.org ✦

    Chai para aserciones
 chaijs.com ✦ Enzyme
 airbnb.io/enzyme/
  17. ENZYME ✦ Shallow rendering
 útil para probar un componente desafectado

    por sus hijos ✦ Mount component
 útil para probar el ciclo de vida completo de un componente
  18. SHALLOW RENDERING import React from 'react'; import { Provider }

    from 'react-redux'; import { expect } from 'chai'; import { shallow } from 'enzyme'; import Main from ‘../main'; import store from 'state/redux-store'; describe( 'Main', () => { it( 'should render the Main component', () => { const component = shallow( <Provider store={ store } ><Main /></Provider> ); expect( component.find( 'Main' ) ).to.exist; }); } );
  19. MOUNT COMPONENT import React from 'react'; import { expect }

    from 'chai'; import { mount } from 'enzyme'; import { Masthead } from '../index'; describe( 'Masthead', () => { it( 'should display the badge in Dev Mode', () => { const component = mount( <Masthead siteConnectionStatus="dev"/> ); expect( component.find( 'code' ) ).to.have.length( 1 ); } ); } ); components/masthead export default connect( state => { return { siteConnectionStatus: getSiteConnectionStatus( state ), // más propiedades }; } )( Masthead );
  20. MOUNT COMPONENT let testProps = { userCanManageModules: false, userCanViewStats: false,

    isModuleActivated: module => false }; const wrapper = mount( <Navigation { ...testProps } /> ); it( 'should render 1 tab: Apps', () => { expect( wrapper.find( 'NavItem' ).children().text() ) .to.be.equal( 'Apps' ); } );
  21. MOUNT COMPONENT let testProps = { userCanManageModules: true, userCanViewStats: false,

    isModuleActivated: module => false }; const wrapper = mount( <Navigation { ...testProps } /> ); it( 'should render 3 tabs: At a Glance, Apps, Plans', () => { expect( wrapper.find( 'NavItem' ).children().map( item => item.text() ).join() ) .to.be.equal( 'At a Glance,Apps,Plans' ); } );