Giter VIP home page Giter VIP logo

fusion-api's Introduction

Fusion

Everything a Fusion app needs to communicate with the core.

Version Downloads/week License

Install

Yarn

$ yarn add @equinor/fusion

NPM

$ npm install @equinor/fusion --save

Usage as App developer

Current user

import React from "react";
import { Spinner } from "@equinor/fusion-components";
import { useCurrentUser } from "@equinor/fusion";

const MyComponent = () => {
    const currentUser = useCurrentUser();

    // Current user might not be retrieved from the cache when the component loads,
    // So check for nulL!
    if(currentUser == null) {
        return <Spinner />;
    }
    
    return (
        <h1>Hi {currentUser.name}!</h1>
    );
};

export default MyComponent; 

Core resources

import React from "react";
import { Spinner } from "@equinor/fusion-components";
import { useCurrentContext, ContextTypes, useHandover, useHandoverMcpkgs } from "@equinor/fusion";

const MyHandoverCommpkgDetails = ({ id }) => {
    const currentProject = useCurrentContext(ContextTypes.PDP);
    const [isFetching, handoverMcpkgs] = useHandoverMcpkgs(currentProject, id);
    
    if(isFetching) {
        return <Spinner />;
    }

    return (
        <ul>
            {handoverMcpkgs.map(mcpkg => (
                <li>
                    {mcpkg.mcPkgNo}
                </li>
            ))}
        </ul>
    );
};

const MyHandoverComponent = () => {
    const currentProject = useCurrentContext(ContextTypes.PDP);
    const [isFetching, handoverData] = useHandover(currentProject);
    
    if(isFetching) {
        return <Spinner />;
    }

    return (
        <ul>
            {handoverData.map(handoverItem => (
                <li>
                    <h2>{handoverItem.commpkgNo}</h2>
                    <MyHandoverCommpkgDetails id={handoverItem.id}  />
                </li>
            ))}
        </ul>
    );
};

export default MyComponent; 

Core settings

Core settings are read-only for apps

import React from "react";
import { useCoreSettings, ComponentDisplayTypes } from "@equinor/fusion";

const MyComponent = () => {
    const coreSettings = useCoreSettings();

    if(coreSettings.componentDisplayType === ComponentDisplayTypes.Compact) {
        return (<span>Looks like you prefere compact mode!</span>);
    } else {
        return (<h2>Some more spacing for you!</h2>)
    }
};

export default MyComponent; 

App settings

App settings are automatically scoped to the current app

import React from "react";
import { Button } from "@equinor/fusion-components";
import { useAppSettings } from "@equinor/fusion";

const MyComponent = () => {
    const [appSettings, setAppSettings] = useAppSettings();

    return (
        <Button
            primary contained
            onClick={() => setAppSettings("toggle", !appSettings.toggle)}
        >
            Click to toggle {appSettings.toggle ? "On" : "Off"}
        </Button>
    );
};

export default MyComponent; 

Usage as Core developer

Bootstrap the fusion core

import React, { useRef } from "react";
import { render } from "react-dom";
import { Router } from "react-router";
import { createBrowserHistory } from "history";
import {
    AuthContainer,
    createFusionContext,
    FusionContext,
    ServiceResolver,
} from "@equinor/fusion";

const serviceResolver: ServiceResolver = {
    getDataProxyUrl: () => "http://api.url.com",
    getOrgUrl: () => "http://api.url.com",
};

const start = async () => {
    const authContainer = new AuthContainer();

    // Handle redirect from login
    await authContainer.handleWindowCallbackAsync();

    // Register the main fusion AAD app (get the client id from config)
    const coreAppClientId = "{client-id}";
    const coreAppRegistered = await authContainer.registerAppAsync(
        coreAppClientId,
        [serviceResolver.getDataProxyUrl(), serviceResolver.getOrgUrl()]
    );

    if(!coreAppRegistered) {
        authContainer.login(coreAppClientId);
    } else {

        const Root = () => {
            const root = useRef();
            const overlay = useRef();
            const fusionContext = createFusionContext(
                authContainer,
                serviceResolver,
                { root, overlay, }
            );

            return (
                <Router history={fusionContext.history}>
                    <FusionContext.Provider value={fusionContext}>
                        <div id="fusion-root" ref={rootRef}>
                            {/* The app component goes here */}
                        </div>
                        <div id="overlay-container" ref={overlayRef}>
                            {/* Leave this empty. Used for dialogs, popovers, tooltips etc. */}
                        </div>
                    </FusionContext.Provider>
                </Router>
            );
        };

        render(<Root />, document.getElementById("app"));
    }
};

start();

Hooks

useAbortableRequest

This hook will make an HTTP callback abortable, i.e allows for cancellation of a pending HTTP request. The HTTP callback in the example below is defined as executeRequest, which will make an HTTP request towards some API client. The callback will be made abortable when the callback is passed onto the useAbortableRequest hook, with the custom abort signal handler onAbort. E.g if the abortable callback onInput initiates a request, but the MyComponents component is unmounted, the initiated HTTP request will be cancelled.

export const MyComponents = () => {
    const {someClient} = useApiClients();
    const [state, setState] = useState();
    const [error, setError] = useState();
    const [loading, setLoading] = useState();
    const executeRequest = useCallback(async(e: Event) => {
        try{
            setLoading(true);
            const response = someClient.getFoo(e.target.value);
            setState(response);
        } catch (e) {
            setError(e.message);
        } finally (){
            setLoading(false);
        }
    }, [someClient]);
    const onAbort = useCallback(() => console.debug('request was aborted'));
    const onInput = useAbortableRequest(executeRequest, onAbort);
    return (
        <div>
            <input type="text" onInput={onInput} />
            <span>${state}</span>
        </div>
}

fusion-api's People

Contributors

abdullzahir avatar andreaspresthammer avatar dependabot-preview[bot] avatar dependabot[bot] avatar eikeland avatar eskil0312 avatar hansdahle avatar ingridklepsvik avatar jardt avatar jaysencpp avatar jsoncpp avatar maoft avatar msalte avatar odinr avatar olerichard avatar sandves avatar siljesiik avatar terjebra avatar tobiasny avatar tony-hagen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fusion-api's Issues

track usage of in ContextManager of ReliableDictionary

The context manager uses ReliableDictionary<ContextCache> which saves the history of used context to local storage.

this feature should be deprecated, but logged that the applications dont uses this feature.
this potential occur when the app uses different version of fusion-api than the fusion-cli

HttpClient.getBlobAsync lacks support for Edge versions < 79

getBlobAsync returns a File and not a Blob, which means that the function will crash when run in an Edge browser with version less than 79 (see https://developer.mozilla.org/en-US/docs/Web/API/File#Browser_compatibility).

Proposing one of the following solutions:

  1. Changing the interface of getBlobAsync to return e.g. { blob, fileName } instead of File. Proposing inclusion of fileName as well, since the caller won't be able to resolve this on its own.
    Should probably also create a new getFileAsync that can be used instead of the previous getBlobAsync implementation. These two new functions will have a more intuitive and correct name according to their return type

  2. Create a new function, e.g. getEdgeBlobAsync, that returns { blob, fileName } instead of File. Not the most elegent approach, but it allows getBlobAsync to remain intact for any potential existing users

useNotificationCenter causing endless loop

If you are using this "notify" (component) in a useCallback or useMemo, it would want the "notify" as a dependency. Using the value or function later on in an useEffect will cause the useEffect to run in an endless loop, since the value of the function/value changes all the time. Removing the notify from the depency array will fix the problem. But an useCallback and useMemo should have all its depencies?

image
image

Compact mode not working

When starting our app on localhost with compact mode selected, all elements are shown in comfortable mode.
After a soft refresh, elements are shown in compact mode on localhost.

Compact mode does not work at all in our CI or production environment.

Occurred after upgrading @equinor/fusion from 3.2.6 to 3.2.8. Same problem in version 3.3.0.

Brgds Trond-Egil Hegerstrøm,

App dev Spine Alarm Management

[Security] Workflow build.yml is using vulnerable action actions/checkout

The workflow build.yml is referencing action actions/checkout using references v1. However this reference is missing the commit a6747255bd19d7a757dbdda8c654a9f84db19839 which may contain fix to the some vulnerability.
The vulnerability fix that is missing by actions version could be related to:
(1) CVE fix
(2) upgrade of vulnerable dependency
(3) fix to secret leak and others.
Please consider to update the reference to the action.

Fusion apps are unable to change context using React Router Link links

Given that I am using the following resolve context function:

registerApp('pitstop', {
    AppComponent: App,
    context : {
        types: [ContextTypes.Project],
        buildUrl: (context: Context | null, url:string) => {
            if (!context) return ''
            return `/${context.id}`
        },
        getContextFromUrl: (url: string) => {
            return url.split('/')[1]
        },
    },
});

When I, as an app developer, create links for my users using React Router with the Link component, e.g.

<Link to={/${x.projectId}/list/${x.id}}>{x.project}</Link>

So that my users can switch to a subpage in a different context.

I expect React to render this automatically.

Instead, the app context (project) is not changed when I access subpages from that context (i.e. .../link/${x.id}).

For this to work, I have to do a hard refresh, or use Link without a subpage reference.

Current workaround is to avoid using Link, and sticking to good old <a href> - but this is not very SPA like.

Duplicate FusionContext

If an app has different API version in their app and fusion-cli, the fusionContext ends up being recreated.

Which again causes issues with events being "dropped", and functions like compact mode stops working.

Part of the problem start in the ensureGlbalFusionContextType in FusionContext.ts

Auth: handleWindowCallbackAsync doesn't always reload to redirectUrl

We have experienced issues when logging in several apps with AuthContainer. The problem we want to fix is that the redirect-url sometimes gets lost inbetween the login-cycles, where the app gets redirected to the front page (app origin URL) after all apps have logged in.

By placing a console.log in (1) app-entry file and (2) AuthContainer.handleWindowCallbackAsync, we found that the redirect-url doesn't always get set when assigning it to window.location.href. The app will not get restarted when this occurs. Neither will the redirectUrl be found in location when the next login starts.

We tested an alternative approach which seems to work every time:
window.history.replaceState(null, "", redirectUrl);
window.location.reload(true);

This forces the page to be reloaded from the server, and the redirectUrl is always pushed to the window's location after handleWindowCallbackAsync

Possible bug in featurelogger.js

When running the org admin app locally periodically i get this error that loops over and over and slows down the application.
image
These are the functions that gets called:
image

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.