Fusion Framework
🚨 WIP🏗
This package is under construction and currently under alpha.
Concept
Services
Services are modules that the framework provides for the consumer.
When initializing the framework, the system creates a object with configurators.
The initiator configures services by adding callbacks for initalaztion of the service.
The system return providers for the configured services.
Expect breaking changes untill stable release!
Provider / Portal
const initialize = async() => {
window.Fusion = await createInstance((build) => {
build.auth.client = createAuthClient(
'my-tennant-id',
'my-client-id',
'/msal/auth'
);
build.http.configureClient('foo', 'https://my.services.com');
build.http.configureClient('bar', (client) => {
client.uri = 'https://my.other-services.com';
client.defaultScope = ['https://somewhere.com/read'];
client.requestHandler.add('custom-headers', (request) => {
const headers = new Headers(request.headers);
headers.append('x-app-version', 'v1.2.3');
headers.append('x-app-env', 'alpha');
return { ...request, headers };
});
});
});
}
Consumer / Application
setup
export const setup = (fusion: Fusion, env: ApplicationManifest) => {
const services = fusion.createServiceInstance((config) => {
config.http.configureClient(
'data-proxy',
'https://somewhere-test.com'
);
config.http.configureClient(
'app-client',
env.endpoints.prod.myService
);
config.state.configureStore(
'my-store',
(action$, state$, services) => {
services.http.createClient('portal').fetch('foo')
}
)
});
return (element: HTMLElement) => {
const content = document.createElement('p');
content.innerText = 'Hello Fusion';
element.appendChild(content);
}
};
HttpClient
services.createClient('bar')
.fetch('/api/apps')
.subscribe(async(x) => console.log(await x.json()));
services.createClient('bar')
.fetchAsync('/api/apps')
.then(async(x) => console.log(await x.json()));
fetch
The fetch method of the client return an Observable reponse.
Observables has the advantage of cancel when unsuscribed.
Secondly we can compose the flow easily with operator functions
React
TODO move to react lib
import { useClient } from '@equinor/fusion-framework-react';
import { Subscription, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
const MyComponent = () => {
const [query, setQuery] = useState('');
const [value, setValue] = useState('');
const client = useClient('my-client');
const input$ = useMemo(() => new Subject(), []);
const onInput = useCallback((value: string) => {
input$.next(value), [input$];
});
useEffect(() =>{
const subscription = new Subscription();
subscription.add(input$.subscribe(setQuery));
subscription.add(input$.pipe(
switchMap(x => client.fetch(`api/foo?q=${x}`)),
switchMap(x => x.json()),
).subscribe(setValue);
return () => subscription.unsubscribe();
}, []));
return <>
<input value={query} onInput={onInput} />
<pre>{value}</pre>
</>
};
RequestHandler
Before a request is executed all registered request handlers are proccessed. The tail of a operator is chain to head of the next.
Handler must return same type as provided RequestInit
or void
and can be async.
type ProcessOperator<T, R = T> = (request: T) =>
R | void | Promise<R | void>;
Handlers are keyed to allow override of existing by client.requestHandler.set
, using client.requestHandler.add
will throw error if allready defined.
client.requestHandler.add('custom-headers', async(request) => {
const values = await import('values.json');
const headers = new Headers(request.headers);
Object.keys(values).forEach(key => {
headers.append(`x-${key}`, values[key]);
});
return {...request, headers};
});