npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@springtree/eva-suite-bootstrap

v2.1.2

Published

Shared bootstrap logic for EVA Suite Apps to setup the React context that interacts with the EVA SDK

Downloads

18

Readme

EVA Suite bootstrap

Shared bootstrap logic for EVA Suite Apps to setup the React context that interacts with the EVA SDK

Installation

npm install @springtree/eva-suite-bootstrap --save

NOTE: A breaking change was made to upgrade to a newer version of the SDK. See the update notes for more details

Usage

This library is intended to be used with a TypeScript based React app from the EVA Suite. When using the library you need to provide:

  • Client details (name, version)
  • Firebase Configuration
  • Redux store interface
  • EVA Storage implementation
  • EVA Login widget instance

Storage

You can use the below code to create your storage implementation. Replace MyApp as needed.

File: context/EvaContext/storage.ts:

import { EvaStorage } from '@springtree/eva-sdk-core-storage';
import { IEvaSuiteStorage } from '@springtree/eva-suite-bootstrap';

export const STORAGE_PREFIX='evaAdminMyApp';

/**
 * This interface defines all the storage used for this application.
 * It includes the auto-synced properties from the EVA SDK store builder
 *
 * @export
 * @interface IEvaSuiteMyAppStorage
 */
export interface IEvaSuiteMyAppStorage extends IEvaSuiteStorage {
  // Declare your storage properties here
  // ex: 'evaAdminMyApp:mySetting': string;
  //
  ...
}

export const evaStorage = new EvaStorage<IEvaSuiteMyAppStorage>();

Store interface

You need to declare the interface of your Redux store. You should extend IEvaReduxStoreDefaultShape for the basics added by the SDK.

File: context/EvaContext/state.ts:

import {
  IEvaReduxStoreDefaultShape,
  IEvaServiceState,
} from '@springtree/eva-sdk-redux-store-builder';
import { Core } from '@springtree/eva-services-core';

export interface IEvaSuiteMyAppStore extends IEvaReduxStoreDefaultShape {

  /**
   * The application configuration will be retrieved after the user logs in
   * as it may contain more data for an employee
   * The base configuration is available on the `EvaEndpoint` if needed
   *
   * @type {IEvaServiceState<Core.GetApplicationConfiguration>}
   */
  'Core:GetApplicationConfiguration': IEvaServiceState<Core.GetApplicationConfiguration>;

  ....
}

Store builder

Next step is to use the storage instance and store definitions to prepare the builder. You will also provide the builder with your EVA Login widget instance which can be styled and configured as required.

File: context/EvaContext/store-builder.ts:

// The EVA Login widget component
//
import EvaLogin from '@bit/springtree_solutions.eva-suite-ui.eva-login';
import '@material-ui/icons/Lock';
import theme from '../../theme/EvaMaterialTheme';

// We need the firebase config for the customer manager login flow
//
import firebaseConfig from '../../utils/firebase/firebaseConfig';

// The store bootstrap helper and our store and storage interfaces
//
import { EvaContextBuilder, IEvaSession } from '@springtree/eva-suite-bootstrap';
import { IEvaSuiteMyAppStore } from './state';
import { IEvaSuiteMyAppStorage } from './storage';

// Helper type for the application specific EVA Session
// with store and storage types set
//
export type IEvaSuiteMyAppSession = IEvaSession<IEvaSuiteMyAppStore, IEvaSuiteMyAppStorage>;

// Client application details from our package file
//
const pkg = require('../../../package.json');

// Our login widget instance
//
const loginWidget = new EvaLogin({
  isModal: true,
  isOpen: false,
  language: 'nl-nl',
  theme,
});

// Our configured store builder
//
export const contextBuilder = new EvaContextBuilder<IEvaSuiteMyAppStore, IEvaSuiteMyAppStorage>({
  firebaseConfig,
  loginWidget,
  appName: pkg.name,
  appVersion: pkg.version,
});

// Our fully typed session manager
//
export const sessionManager = new EvaSessionManager<IEvaSuiteMyAppStore, IEvaSuiteMyAppStorage>();

Store

And finally you will add all your EVA Services, custom reducers and logic to the builder to generate the store.

File: context/EvaContext/store-builder.ts:

// The EVA Login widget component
//
import EvaLogin from '@bit/springtree_solutions.eva-suite-ui.eva-login';
import '@material-ui/icons/Lock';

// We need the firebase config for the customer manager login flow
//
import firebaseConfig from '../../utils/firebase/firebaseConfig';

// The store bootstrap helper and our store and storage interfaces
//
import { EvaContextBuilder, IEvaSession, EvaSessionManager } from '@springtree/eva-suite-bootstrap';
import { IEvaSuiteMyAppStore } from './state';
import { IEvaSuiteMyAppStorage } from './storage';

// Helper type for the application specific EVA Session
// with store and storage types set
//
export type IEvaSuiteMyAppSession = IEvaSession<IEvaSuiteMyAppStore, IEvaSuiteMyAppStorage>;

// Client application details from our package file
//
const pkg = require('../../../package.json');

// Our login widget instance
//
const loginWidget = new EvaLogin({
  isModal: true,
  isOpen: false
});

// Our configured store builder
//
export const contextBuilder = new EvaContextBuilder<IEvaSuiteMyAppStore, IEvaSuiteMyAppStorage>({
  firebaseConfig,
  loginWidget,
  appName: pkg.name,
  appVersion: pkg.version,
});

// Our fully typed session manager
//
export const sessionManager = new EvaSessionManager<IEvaSuiteMyAppStore, IEvaSuiteMyAppStorage>();

The definition of store reducers and logic can be best isolated in it's own file.

File: store.ts:

import { Core } from '@springtree/eva-services-core';
import { Admin } from '@springtree/eva-services-admin';
import { PIM } from '@springtree/eva-services-pim';

import { createChainLogic } from '@springtree/eva-sdk-redux-store-builder';
import { Logic } from 'redux-logic';
import { contextBuilder } from './store-builder';

console.log( '[EVA-CONTEXT]: Configuring store...' );

////////////////////////////////////////////////////////////////////////////////
// EVA Services
////////////////////////////////////////////////////////////////////////////////
//
contextBuilder.addEvaService( 'Admin:AdminGetAllModules', Admin.AdminGetAllModules );

// Reducers for all the Core services
//
contextBuilder.addEvaService( 'Core:GetApplicationConfiguration', Core.GetApplicationConfiguration);

...

// Custom services
//
contextBuilder.addEvaService( 'Custom:ListDiscountOrderTypes', Core.ListEnumValues );

////////////////////////////////////////////////////////////////////////////////
// Custom reducers
////////////////////////////////////////////////////////////////////////////////
//

////////////////////////////////////////////////////////////////////////////////
// Custom logic
////////////////////////////////////////////////////////////////////////////////
//
// Logic chains for all the Core services
//
// Fetch the ApplicationConfiguration when current user is logged in as an employee
//
const fetchApplicationConfigurationOnUserLogin =   createChainLogic({
  from: {
    service: Core.GetCurrentUser,
    stateOptions: { stateKey: 'currentUser' },
    type: 'RESPONSE',
  },
  to: {
    service: Core.GetApplicationConfiguration,
    validate: (currentUserState) => {
      const currentUserDetails = currentUserState ? currentUserState.data.response : undefined;
      if (!currentUserDetails) {
        return false;
      }

      // Must be an employee
      //
      const userType = currentUserDetails.User.Type;
      const isEmployee = (userType & 1) !== 0;
      return isEmployee;
    },
  },
});

contextBuilder.addLogic( fetchApplicationConfigurationOnUserLogin as Logic[] );

Service helpers and hooks

For convenience you will likely add service specific hooks in hooks/... and service access helper in service-helpers.ts. The hooks are React app specific but the service helpers follow a standard pattern described below.

File: context/EvaContext/service-helpers.ts:

import { createServiceContext } from '@springtree/eva-suite-bootstrap';
import { IEvaSuiteMyAppSession } from './store-builder';

// Import the EVA Service definitions you need here
//
import { Core } from '@springtree/eva-services-core';

// Export all service context helpers for easy consumption in our React components
// These will also be handy to see where each service is being used
// You can use 'find all references' on them in VSCode for instance
//
export function coreGetApplicationConfiguration(session: IEvaSession) {
  return createServiceContext( {
    session,
    path: 'Core:GetApplicationConfiguration',
    service: Core.GetApplicationConfiguration,
  });
}

// Add more service helpers here...

React context provider

The context can be made available to the React with the following code:

import React, { useEffect, useState, ComponentProps } from 'react';
import { IEvaSuiteMyAppSession, contextBuilder, sessionManager } from './store-builder';
import { evaStorage } from './storage';

console.log( '[EVA-CONTEXT]: Initializing...' );

// Import your store reducers and logic definitions for the builder
//
import './store';

/** Create React context */
export const EvaSession = React.createContext<IEvaSuiteMyAppSession>(sessionManager.currentSession as IEvaSuiteMyAppSession);

/**
 * This is the interface for the `Data` property return from the `Admin.AdminGetAllModules` service
 * This property is managed by the front-ends and passed along by the backend
 *
 * @export
 * @interface IEvaAdminModuleData
 */
export interface IEvaAdminModuleData {
  Actions: Array<{ Label: string, Url: string }>;
  Description: string;
  Url: string;
  Title: string;
  Version?: string;
}

/**
 * The React component properties for the EVA Context
 *
 * @interface EvaContextProps
 * @extends {ComponentProps}
 */
interface EvaContextProps extends ComponentProps<any> {};

/* Create the React context element */
const EvaContext = (props: EvaContextProps) => {

  console.log('[EVA-CONTEXT]: Constructing EVA context');
  const [session, setSession] = useState();

 /** Debug logging of session changes */
  useEffect( () => {
    console.log('[EVA-CONTEXT]: Session updated', session);
  }, [session] );


  /** On mount bootstrap the store */
  useEffect( () => {
    // Create an EVA Session
    // We will not await it so that the app and the backend can initialise at the same time
    // Also by not setting a user token it will be fetched from localStorage by default
    // Persisting to localStorage
    //
    console.log( '[EVA-CONTEXT]: Creating EVA Session for context' );
    sessionManager.createSession(
      contextBuilder,
      {
        storage: evaStorage,
      },
    )
    .then( ( evaSession ) => setSession( evaSession ) )
    .catch( ( error ) => {
      console.error( '[EVA-CONTEXT]: Failed to create EVA Session for context', error );
    } )
    ;
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [] );

  // You can add the provider in your App.tsx
  //
  // ...
  // <EvaContext>
  //   <TranslationProvider>
  //     <EvaHeader appName={'Promotion Engine'} />
  //     <Box display={'flex'}>
  //       <EvaSideNav routes={this.routes} activeRoute={this.activatedRoute} />
  //       <Routes></Routes>
  //     </Box>
  //   </TranslationProvider>
  // </EvaContext>
  // ...
  //
  return (
    <EvaSession.Provider value={sessionManager.currentSession as IEvaSuiteMyAppSession}>
      {session ? ( props.children ) : (
        <span>loading...</span>
      )}
    </EvaSession.Provider>
  );
};

export default EvaContext;

/** Export all the service context helpers */
export * from './service-helpers';

Test

npm run test