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

Developing in MapStore - Part 2 - Architecture/Building/Deploying/Developing

Developing in MapStore - Part 2 - Architecture/Building/Deploying/Developing

Third module of our Developers Training for MapStore: Architecture/Building/Deploying/Developing

Simone Giannecchini

February 11, 2021

More Decks by Simone Giannecchini

Other Decks in Technology


  1. MapStore Architecture Front-end • Uses ◦ Javascript (EcmaScript) ◦ React

    ◦ Redux - with both thunk and redux-observable middlewares • Provides ◦ A plugin system that allows to develop separated components to add to the application ◦ StandardApp and StandardStore to build your custom application in minutes as a framework ◦ A project creation system to create your own project based on MapStore, without forking • Is Mapping library Agnostic ◦ Mapping library is wrapped in react components
  2. MapStore Architecture Back-end • Uses ◦ MapStore back-end (Java /

    Spring MVC) ◦ GeoStore (external application merged-in) ◦ http-proxy (external application merged-in) • Provides ◦ MapStore Back-end: ▪ Base services to support extensions and data dir system (introduced in 2020.02.00) ◦ GeoStore: ▪ Base services for authentication and authorization (resource sharing) and accounting ▪ Base support for integration with GeoServer/LDAP ◦ http-proxy ▪ Secured proxy to communicate with external services without CORS.
  3. MapStore - Building Build / Testing • Using npm for

    JS dependencies • Using babel for compatibility with all browsers • Using webpack as module bundler (and dev server, with proxy) • Using karma as unit test runner • Using maven to Java dependencies and to build the final web application babel loader JS html less /css DIST maven Java MapStore.war webpack less loader
  4. MapStore - Building • build.sh is the main script used

    to build the application • It runs mainly (with some additional options and things, like test running and documentation generation) these commands: babel loader JS html less /css DIST maven Java MapStore.war npm install # download npm dependencies npm run compile # build JS/HTML/CSS bundled mvn install #download JAVA dependencies, build the final war archive in web/target webpack less loader
  5. MapStore - Start developing Clone MapStore from GitHub Cd into

    the new directory and run npm install to download dependencies cd MapStore2 npm install git clone https://github.com/geosolutions-it/MapStore2
  6. MapStore - Deploying • You can install MapStore.war in your

    own servlet container (e.g. Tomcat) • By default it uses H2 as Database. This configuration is not suggested in production. On documentation you can find the guide to connect it to Postgres or Oracle. • On the Documentation you can also find how to integrate users with GeoServer and/or LDAP. • By default there are two users in a new MapStore instance: ◦ admin, a user with ADMIN role, with password admin ◦ user, a user with USER role, with password user • You can use a running instance of MapStore in a tomcat as your own development back-end service
  7. MapStore - Running the front-end in development mode Most of

    the development is usually on the front-end side. To start you have simply to run : This command start the webpack-dev-server on http://localhost:8081 npm install # at least once at the beginning, or when some dependency changes npm start http://localhost:8081
  8. MapStore - Debugging on front-end Adding redux developer tools extension

    to your browser and adding debug=true to the query string you can monitor all redux actions and state modification during the execution of MapStore in development. This is very useful to understand how everything happens in the whole application and to verify the execution of your own actions, reducers, epics, plugins
  9. MapStore - Debugging on front-end You can debug your code

    using the browser’s dev tool. Note webpack provides several ways to build the code for debug source mapping. MapStore uses the devtool: “eval” mode that is a good compromise between performances and code readability. (you can find this configuration in buildConfig.js)
  10. MapStore - Webpack dev-server and proxy The webpack dev-server proxy

    in MapStore is configured to use as back-end MapStore dev instance at https://dev.mapstore.geo-solutions.it/ You can use your own test instance by modifying the dev-server configuration, configured to proxy the back-end as indicated in the build/buildConfig.js file. devServer : { proxy: proxy || { '/rest': { // replace all dev.mapstore with your own server address ... target: "https://dev.mapstore.geo-solutions.it/mapstore" , secure: false, headers: { host: "dev.mapstore.geo-solutions.it" } }, '/proxy' : { target: "https://dev.mapstore.geo-solutions.it/mapstore" , secure: false, headers: { host: "dev.mapstore.geo-solutions.it" } }, // more entries ... } } buildConfig.js is a utility function that creates webpack configurations for MapStore. It is used by build/webpack.config.js and build/prod-webpack.config.js that are the main entry points to build the application.
  11. MapStore - Debugging the back-end MapStore Back-end is a Java

    Application that uses Spring. So standard techniques to run and debug Java applications are valid. For instance Eclipse remote debugging with Tomcat using JPDA You can import the code of MapStore (or your custom project), geostore, etc… in Eclipse IDE by running: This creates the projects that can be imported in the Eclipse IDE. Note You may not need to develop on MapStore back-end. You can use instead your favourite back-end framework, and integrating authentication using database and/or REST services (GeoServer integration uses this techniques). mvn eclipse:clean eclipse:eclipse
  12. MapStore Folder Structure web/client contains all the front-end framework files

    (javascript) • components, actions, reducers, epics • plugins, that connect all things together • Pages (managed by react-router) + some plugins are also plugins containers • product contains the configuration and the plugins that belongs to MapStore product (event the set of plugins). Most of the times you don’t care about pages, stores, etc... You will have only to develop your own plugins
  13. MapStore - Develop a plugin A MapStore plugin is the

    main development unit used to add functionalities. • The simplest plugin you can imagine is a react component rendered in the page. // web/client/plugins/Example.jsx import React from 'react'; import { createPlugin } from '../utils/PluginsUtils'; const style = { position: "absolute", background: "white", top: 50, left: 50 }; const Component = () => <div style={style}>Hello</div>; // export the plugin export default createPlugin('Example', { component: Component }); Note You will find several ways how plugins are exported inside the framework and inside the examples. The suggested way is now using createPlugin from web/client/utils/pluginUtils
  14. MapStore - Develop a plugin To develop a plugin you

    have to do 3 things: 1. create a file that exports the plugin in the defined format, for instance (web/client/)plugins/Example.jsx 2. add the plugin in your (web/client/product/plugins.js file (in product folder, for the) 3. configure it in (web/client/)localConfig.json in plugins/desktop array // web/client/product/plugins.js module. exports = { plugins : { // ... other plugins ExamplePlugin : require ('../plugins/Example' ).default }, // ... }; // web/client/localConfig.json { "plugins": { "desktop": [ // ...other plugins configuration // add only the plugin name to make it available "Example" // OR add an object with name: "PluginName" { "name": "Example", // Object allows this "cfg" and other configurations // all props declared in cfg are passed to the component "cfg": {"prop1": "value1"} } ] } Note the require in plugins.js MapStore make mixed use of import and requires, this duality will be removed in favor of imports in the next release, thanks to the dynamic import support introduced by ES11
  15. localConfig.json is the file with base settings and where all

    the plugins for all the pages are configured. plugins → desktop are the plugins of the map viewer in desktop mode. plugins → mobile are the plugins of the map viewer in the mobile mode. More information about the localConfig.json file are in the related documentation. Creating the Example.jsx plugin, including it in the product/plugins.js and configuring it in localConfig.json → plugins → desktop will make the plugin available in the default MapViewer. MapStore - Develop a plugin
  16. MapStore - Develop a plugin You can improve your plugin:

    • connecting to the application state and make it able to dispatch actions (using react-redux connect) • Declaring the used epics and reducers, so they will be included • Declaring how it is wired with other plugins, if present There are several configurations and options you can use to set-up your plug-in You can follow the “How To” dedicated to plugins to explore all these possibilities. import Component from '../components/MyComponent' ; import myReducer from '../reducers/myReducer; import * as epics from '../epics/myEpics' ; // connect the plugin to state and action dispatch const Plugin = connect ( // 1st argument: mapStateToProps binds redux state to react props // you can use existing web/client/selectors and reselect lib here state => ({ property1 : state.myReducer .value1 }), // 2nd argument: mapDispatchToProps maps handlers { // Returned values will be dispatched as actions onClick : () => myAction () } )(Component); // export the plugin. export default createPlugin ('Example' , { component : Plugin, // declare reducers reducers : { myReducer : myReducer } // declare epics epics, // declare wiring with other plugins containers : { Plugin2 : { // name of the plugin to use // values here depends on Plugin2 } } });
  17. MapStore Project MapStore allows to create custom projects using the

    standard one as a framework. In a project you can customize whatever you want (build units, plugins, theme, back-end ...) To create a project you can use the project creation script createProject.js # createProject.js will prompt some questions $ node ./createProject.js Project Type (standard): # ← press enter to use defaults between ( ) Project Name: MyProject # ← Name of the project Project Version (1.0.0): Project Description (Project Name): Repository URL: # Optional Output folder: ../MyProject # ← destination folder Out folder created (../MyProject) Creating package.json... package.json file created Copying static files... Copied .editorconfig # … # … some other logs … # … Templates copied Creating git repo... git repo OK! $ cd ../MyProject $ npm install # … $ npm start # … [./node_modules/css-loader/lib/css-base.js] 1.47 KiB {mini-css-extract-plugin} [built] ℹ 「wdm」: Compiled with warnings.
  18. MapStore Project The project creation script creates a directory structure,

    with MapStore included as git sub-module, that you can use as scheleton to start developing your custom project • js directory can contain your custom client code (plugins…) • backend folder can contain your custom back-end code, if needed ├── assets # you can put here assets (images, css ...) ├── backend # your custom back-end can be placed here │ └── src ... ├── build.sh # same build script of the main product # configuration files for maps and application ├── config.json ├── new.json ├── ... ├── js # custom front-end code can be placed here │ └── app.jsx # main entry point of your app <-- ├── MapStore2 # Mapstore Git sub-module # main html pages and templates, similar to MapStore ├── index.html ├── ... ├── web │ ├── pom.xml │ └── src/main │ ├── resources # back-end configuration files and overrides │ │ ├── geostore-datasource-ovr.properties │ │ ├── ... │ │ │ ├── proxy.properties │ │ │ └── sample_categories.xml │ └── webapp │ └── WEB-INF │ ├── dispatcher-servlet.xml │ └── web.xml ├── package.json ├── … # some other files for build, config etc… similar to MapStore └── webpack.config.js
  19. MapStore Project • running build.sh you will produce a <projectName>.war

    package in web/target • running npm start you can run the application in debug mode. • You can customize the proxy-dev server by adding a proxy configuration adding a last argument of buildConfig call in webpack.config.js • You can customize the application from js/app.jsx // webpack.config.js const path = require("path"); const themeEntries = require('./MapStore2/build/themes.js' ).themeEntries ; const extractThemesPlugin = require('./MapStore2/build/themes.js' ).extractThemesPlugin ; module.exports = require('./MapStore2/build/buildConfig' )( { 'MyProject' : path.join(__dirname , "js", "app"), 'MyProject-embedded' : path.join(__dirname , "MapStore2" , "web", "client" , "product" , "embedded" ), 'MyProject-api' : path.join(__dirname , "MapStore2" , "web", "client" , "product" , "api") }, themeEntries, { base: __dirname , dist: path.join(__dirname , "dist"), framework : path.join(__dirname , "MapStore2" , "web", "client" ), code: [path.join(__dirname , "js"), path.join(__dirname , "MapStore2" , "web", "client" )] }, extractThemesPlugin, false , "dist/", '.MyProject' , [], { "@mapstore" : path.resolve(__dirname , "MapStore2" , "web", "client" ), "@js": path.resolve(__dirname , "js") }, /* HERE ADD YOUR CUSTOM PROXY CONFIG */ );
  20. MapStore Project js/app.jsx is the entry point of your custom

    project. You will find comments inside it about how to customize all the parts. For instance you can add your own plugins by customizing const plugins = require('@mapstore/product/plugins' ); with your own plugins file const plugins = require('/plugins' ); const ConfigUtils = require('@mapstore/utils/ConfigUtils' ); /** * Add custom (overriding) translations with: * * ConfigUtils.setConfigProp('translationsPath', ['./MapStore2/web/client/translations', './translations']); */ ConfigUtils .setConfigProp ('translationsPath' , './MapStore2/web/client/translations' ); ConfigUtils .setConfigProp ('themePrefix' , 'MyProject' ); /** * Use a custom plugins configuration file with: * * ConfigUtils.setLocalConfigurationFile('localConfig.json'); */ ConfigUtils .setLocalConfigurationFile ('MapStore2/web/client/localConfig.json' ); // ... const appConfig = require('@mapstore/product/appConfig' ); /** * Define a custom list of plugins with: * * const plugins = require('./plugins'); */ const plugins = require('@mapstore/product/plugins' ); require('@mapstore/product/main' )(appConfig, plugins); Note @mapstore is a shortcut for MapStore2/web/client
  21. MapStore Project Frontend runtime dependencies are inherited from the MapStore2

    submodule by referring it as a single file entry in package.json. Developer dependencies are instead independently listed (this is probably going to change soon…). If your project needs its own additional dependencies, just add them there. … …
  22. Resources • MapStore 2 Dev guide: https://mapstore.readthedocs.io/en/latest/developer-guide/ • MapStore 2

    Github repository: https://github.com/geosolutions-it/MapStore2 • Sample playground project https://github.com/geosolutions-it/mapstore-playground