Giter Site home page Giter Site logo

ozwillo-portal's People

Contributors

bihane avatar bobeal avatar dependabot[bot] avatar farahcodes avatar franck-boucher avatar ilucatero avatar mkalam-alami avatar mle-astek avatar nabdali avatar nicoletti-seb avatar provto avatar sebukeino avatar silently avatar snyk-bot avatar zagoa avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ozwillo-portal's Issues

Automatic token renewal

Automatic token renewal has been implemented in Citizen Kin. Reuse this implementation in the Portal (not the case presently).

App management prototype: instances and services

Following up on #10 we have created instances and services. For now public services are visible in the app store and private services are automatically added to the manager's dashboard.

Implement a prototype of the App management view that lets IT Managers view all the instances their organizations have bought and manage them and their services (i.e. publish services on the store, subscribe org users to the app, …)

Manage icon variants

Right now the system allows for 64×64 PNG icons for dashboard, app store, my apps, etc. Ideally we should be able to handle variants, i.e. display a 32×32 icon in My Apps, use blown-up icons elsewhere, etc. That means we should provide means for the admin to enter / upload multiple icon variants (and the portal itself should be able to do downscaling at least).

Draft app store

Create a draft application store so it can be demoed and explained to project partners.

App store and account

When connected as citizen (Alice) and going to the application store, Citizen Kin appears as "installed"

CMS back office

Create a simple back office to let administrators edit the content (and view missing translations) - ideally with a split-screen UI whereby content managers enter Markdown content and have a live rendition on a second pane.

Specific error page for instance creation error

The story:

  • user buys an application
  • the kernel sends a request to the app factory
  • the app factory for some reason (valid or otherwise) throws an error (returns anything but a 2xx)
  • the kernel returns an error (502 typically) to the portal

Then the portal should display a specific error page ("Your application purchase could not be completed") rather than redirect to the generic error page ("Oops, there has been an error").

NB - obviously this doesn't catch errors after the app factory has returned and before it has acquitted the instance creation. That's another topic; at least this way we can handle errors from synchronous app factories (e.g. Citizen Kin and others where instantiation == add a row in a database).

Upload service icons

In the fix for #39 we allow to set a distant URL for the service icon. We should also allow users to upload icons straight from the portal, which would then self-host the icons (in the service definition, you'd have Portal URLs for icons).

UI-wise we should make it explicit that we save either a "foreign" icon URL or a direct upload.

Technically it's not too hard, we only need a complementary method in IconService that allows to set an icon without downloading.

Clean up Maven build

When we're out of the first sprint, take a few minutes to clean up the build:

  • use a common parent (no more Spring Boot parent)
  • fix dependency management to make sure all libraries are loaded in the same version
  • preconfigure plugins (build plugin to Java 8, spring boot plugin, etc.)

Automatically log into portal if a kernel session is active

Also, use the kernel suggestion for setting the initial language, by having the kernel extend the login failure response.

@tbroyer quote :

Ideally, I think the Portal should initially do a login check with prompt=none so that if the user is already authenticated at the Kernel, it'll be automatically logged into the portal. We discussed it with @schambon and agreed it would be the best implementation, but it's not necessarily easy to implement so we were OK delaying it. The idea was to extend the error response with a locale parameter.

l10n in apps management

Currently it's only possible to enter one name, description, icon for a service, and the code does in fact push forcefully that one version into all language versions.

We must update the UI to allow multiple-language entering.

KPI: track and report max threads / day

Track and report server load, ideally in terms of peak session count / day; it seems that Spring Boot already provides peak threads which can be used as a proxy to start with.

Application icons

Catalogue entries provide icons:

  • a default icon
  • optionally one icon per language

Icons are provided in the form of URLs. The Portal should not make any assumption about these URLs beyond the fact that they point to a 64×64 PNG.

Create a service to fetch these icons, store them in a portal-local cache, and serve them to clients:

  • the cache should be periodically refreshed, but we can probably safely assume that icon files do not change often (if app providers need to change the icon, they should change the icon URL in the catalog)
  • the cache should be cleaned up (for instance: icons not queried for a month can be deleted)
  • the serving mechanism must support HTTP client-side caching (provide etags and respond with 304 Not Modified)

sign out null

When you're logged to the portal and goes on the portal homepage, the sign out button says "sign out null"

Dashboard: use correct URLs

Currently the service descriptions used to build the Dashboard are taken off the /m/search kernel endpoint.
That endpoint does not in fact return the service_uri - so use the relevant endpoints.

Use notification URI in the Dashboard

If there is a non-zero notification count for a Dashboard icon,
And the service associated with that icon has a non-null notification_uri
Then that icon's link should be set to the notification_uri.

Drag and drop failed on dashboard

it's possible to make a drag and drop of an icon from the principal dashboard to a another one, but it's not possible to change again

Use profile-stored language preferences

Locales (and time zones) are stored in the kernel profile, and returned by the userinfo endpoint (presumably under the "profile" scope).

Use that info to display text and date/times with the correct settings after login.

Token refresh exception is caught by the abstract PortalController

The TokenRefreshInterceptor works by throwing a specific RuntimeException that is caught by the OasisExceptionTranslationFilter - only PortalControl has a catch(Throwable t) (!) that causes the exception to be caught and the error page to be displayed, instead of a new token to be transparently negotiated.

First level caching

Implement a first-level (request-scoped) cache at all data access points in the portal, so that multiple calls to the same data access service (either backed by Mongo, Kernel service, or Data core service) do not translate to as many calls to the backing repository.

This cache should be ephemeral (request-bound) and transparent to user code. Look into using Spring's @Cacheable annotations using a custom CacheManager that uses a request-scoped bean to provide the necessary functionality.

Back to "My Ozwillo" after 1 hour: error 500

When I log to ozwillo-preprod portal and then let my session "sleep" for at least 1 hour (I have not measured exactly), when I come back to the portal homepage and click on "My Ozwillo" I've got an error:

HTTP Status 500 - Request processing failed; nested exception is java.lang.NullPointerException

type Exception report

message Request processing failed; nested exception is java.lang.NullPointerException

description The server encountered an internal error that prevented it from fulfilling this request.

exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
    org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
root cause

java.lang.NullPointerException
    java.util.Objects.requireNonNull(Objects.java:203)
    java.util.Arrays$ArrayList.<init>(Arrays.java:3807)
    java.util.Arrays.asList(Arrays.java:3794)
    org.oasis_eu.portal.core.dao.impl.SubscriptionStoreImpl.findByUserId(SubscriptionStoreImpl.java:39)
    org.oasis_eu.portal.services.PortalDashboardService.getDashboardEntries(PortalDashboardService.java:113)
    org.oasis_eu.portal.services.PortalDashboardService$$FastClassBySpringCGLIB$$39a53ebb.invoke(<generated>)
    org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
    org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    org.oasis_eu.portal.services.PortalDashboardService$$EnhancerBySpringCGLIB$$b978d565.getDashboardEntries(<generated>)
    org.oasis_eu.portal.front.my.MyOzwilloController.myOzwillo(MyOzwilloController.java:61)
    sun.reflect.GeneratedMethodAccessor125.invoke(Unknown Source)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:483)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
    org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
note The full stack trace of the root cause is available in the Apache Tomcat/7.0.52 logs.

Apache Tomcat/7.0.52```

CMS front office enhancements

Currently embedding a content item in a portal page is very manual (one has to call the ContentRenderingService from the controller and pass the result back to Thymeleaf).

Create a "cms" Thymeleaf dialect that would take the call to ContentRenderingService out of Spring controllers. This way, to embed a content item in a page, one should only use cms:content="content_id" in a div.

CMS pages - RESTful locale resolution

For public pages, when there is no user logged in, use a URL path variable to decide on the language to display the page in.

E.g. : /{page_name}/{lang?}/{page_id}

so /press/en/aaa-bbb-ccc would display the page in English, while /presse/fr/aaa-bbb-ccc would display it in French (an additional advantage of that is to make it possible to have localized URLs: "press" vs "presse").

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.