Skip to content

Instantly share code, notes, and snippets.

@kendrelaxman
Last active June 7, 2023 09:15
Show Gist options
  • Save kendrelaxman/baf09e74ff535059cb773c9838ee9188 to your computer and use it in GitHub Desktop.
Save kendrelaxman/baf09e74ff535059cb773c9838ee9188 to your computer and use it in GitHub Desktop.
openfin-wrapper
import { Identity, ChannelClient } from 'openfin';
class OpenFinChannelClient {
private channelClient: ChannelClient;
constructor(providerIdentity: Identity, channelName: string) {
this.channelClient = new ChannelClient(providerIdentity, channelName);
}
public async connect(): Promise<void> {
await this.channelClient.connect();
}
public async disconnect(): Promise<void> {
await this.channelClient.disconnect();
}
public subscribe<T>(topic: string, listener: (message: T, uuid: string, name: string) => void): void {
this.channelClient.subscribe(topic, listener);
}
public unsubscribe(topic: string): void {
this.channelClient.unsubscribe(topic);
}
public publish<T>(topic: string, message: T): void {
this.channelClient.publish(topic, message);
}
}
export default OpenFinChannelClient;
/////////////////////////////
import { fin } from 'openfin';
import OpenFinChannelClient from './OpenFinChannelClient';
async function connectToProvider() {
const providerIdentity = await fin.Application.wrap({ uuid: 'provider-uuid', name: 'provider-name' });
const channelName = 'my-channel';
const client = new OpenFinChannelClient(providerIdentity, channelName);
await client.connect();
client.subscribe<string>('my-topic', (message, uuid, name) => {
console.log(`Received message ${message} from ${name} (${uuid})`);
});
}
connectToProvider();
/////////////
import { fin } from 'openfin';
const channelName = 'my-channel';
const providerIdentity = fin.Application.getCurrent();
const channel = await fin.InterApplicationBus.Channel.create(channelName);
// Publish a message with the topic 'my-topic'
const message = 'Hello, world!';
channel.publish('my-topic', message, providerIdentity.uuid);
//////////////
import { fin } from 'openfin';
class OpenFinChannelProvider {
private channel: fin.InterApplicationBus.Channel;
private providerIdentity: fin.Identity;
constructor(channelName: string) {
this.channel = fin.InterApplicationBus.Channel.create(channelName);
this.providerIdentity = fin.Application.getCurrent();
}
public async publishMessage<T>(topic: string, message: T): Promise<void> {
await this.channel.publish(topic, message, this.providerIdentity.uuid);
}
}
export default OpenFinChannelProvider;
//////////////////
import React from 'react';
import OpenFinChannelProvider from './OpenFinChannelProvider';
class MyProviderComponent extends React.Component {
private channelProvider: OpenFinChannelProvider;
constructor(props: any) {
super(props);
this.channelProvider = new OpenFinChannelProvider('my-channel');
}
public async componentDidMount() {
// Publish a message with the topic 'my-topic'
const message = 'Hello, world!';
await this.channelProvider.publishMessage('my-topic', message);
}
public render() {
return (
<div>
// ...
</div>
);
}
}
export default MyProviderComponent;
////////
import React from 'react';
import { fin } from 'openfin';
interface IState {
messages: string[];
}
class MySubscriberComponent extends React.Component<{}, IState> {
private channelName = 'my-channel';
private subscriberIdentity = fin.Application.getCurrent();
constructor(props: any) {
super(props);
this.state = {
messages: [],
};
// Create a new instance of the ChannelClient class and subscribe to messages
const client = new fin.InterApplicationBus.ChannelClient({ uuid: this.subscriberIdentity.uuid });
client.onChannelConnect(this.channelName, () => {
client.subscribe(this.channelName, (topic, message, uuid) => {
this.handleMessageReceived(topic, message, uuid);
});
});
}
private handleMessageReceived(topic: string, message: any, uuid: string) {
console.log(`Received message: ${message}`);
// Add the message to the component state
this.setState((prevState) => ({
messages: [...prevState.messages, message],
}));
}
public render() {
return (
<div>
<h2>Messages:</h2>
<ul>
{this.state.messages.map((message) => (
<li key={message}>{message}</li>
))}
</ul>
</div>
);
}
}
export default MySubscriberComponent;
////
import { Identity, InterApplicationBus } from 'openfin/_v2/api/interappbus';
interface WrapperProps {
appId: string;
}
class Wrapper {
private _bus: InterApplicationBus;
private _identity: Identity;
constructor(props: WrapperProps) {
if (typeof fin === 'undefined') {
throw new Error('OpenFin API not available');
}
this._bus = fin.InterApplicationBus;
this._identity = { name: props.appId, uuid: fin.me.uuid };
}
public async authenticate(): Promise<boolean> {
try {
await fin.System.authenticate(this._identity);
return true;
} catch (error) {
console.error('Error authenticating with OpenFin:', error);
return false;
}
}
public async send<T>(topic: string, message: T, targetIdentity: Identity): Promise<void> {
try {
await this.authenticate();
await this._bus.send(targetIdentity, topic, message);
} catch (error) {
console.error('Error sending message:', error);
}
}
public async unsubscribe(topic: string, listener: Function): Promise<void> {
try {
await this.authenticate();
await this._bus.unsubscribe(this._identity, topic, listener);
} catch (error) {
console.error('Error unsubscribing from topic:', error);
}
}
public async subscribe<T>(topic: string, listener: (message: T, uuid: string, name: string) => void): Promise<void> {
try {
await this.authenticate();
await this._bus.subscribe(this._identity, topic, listener);
} catch (error) {
console.error('Error subscribing to topic:', error);
}
}
}
export default Wrapper;
//
import { useEffect, useRef } from 'react';
function useIABTopic(topicName, callback) {
const callbackRef = useRef(callback);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
useEffect(() => {
const handleTopicMessage = (message, uuid, name) => {
callbackRef.current(message, uuid, name);
};
fin.InterApplicationBus.subscribe(topicName, handleTopicMessage);
return () => {
fin.InterApplicationBus.unsubscribe(topicName, handleTopicMessage);
};
}, [topicName]);
}
export default useIABTopic;
//
import { useEffect, useRef } from 'react';
function useIABTopic(topicName, callback) {
const callbackRef = useRef(callback);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
useEffect(() => {
const handleTopicMessage = (message, uuid, name) => {
callbackRef.current(message, uuid, name);
};
fin.InterApplicationBus.subscribe(topicName, handleTopicMessage);
return () => {
fin.InterApplicationBus.unsubscribe(topicName, handleTopicMessage);
};
}, [topicName]);
}
export default useIABTopic;
@kendrelaxman
Copy link
Author

function listen() {
const subject = new Subject();
// some code here that calls subject.next
return subject.asObservable();
}

@kendrelaxman
Copy link
Author

import { useState, useEffect } from 'react';

function useMyCustomClass() {
const [instance, setInstance] = useState(null);

function getInstance() {
if (instance) {
return instance;
}

const newInstance = new MyClass();
setInstance(newInstance);
return newInstance;

}

useEffect(() => {
return () => {
if (instance) {
// destroy the instance when the hook is unmounted
instance.destroy();
}
};
}, [instance]);

return getInstance;
}

class MyClass {
// Define your class methods and properties here

destroy() {
// Define a method to destroy the instance if needed
}
}

export default useMyCustomClass;

@kendrelaxman
Copy link
Author

function MyComponent() {
const getInstance = useMyCustomClass();
const myInstance = getInstance();

// Use myInstance here
}

@kendrelaxman
Copy link
Author

import React, { useState } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';

const MyGridComponent = () => {
const [rowData, setRowData] = useState([]);

const onGridReady = (params) => {
const { api } = params;

// Set up your column definitions and other configuration options here
// ...

// Set up the initial data for the grid
const initialData = [
  { make: 'Toyota', model: 'Celica', price: 35000 },
  { make: 'Ford', model: 'Mondeo', price: 32000 },
  { make: 'Porsche', model: 'Boxter', price: 72000 },
];
setRowData(initialData);

// Set up the grid's onSortChanged event listener
api.addEventListener('sortChanged', () => {
  const sortedData = [];
  for (let i = 0; i < api.getDisplayedRowCount(); i++) {
    sortedData.push(api.getDisplayedRowAtIndex(i).data);
  }
  console.log('Sorted Data:', sortedData);
});

};

return (
<div className="ag-theme-alpine" style={{ height: '200px', width: '600px' }}>

{/* Add your column definitions here */}


);
};

export default MyGridComponent;

@kendrelaxman
Copy link
Author

// Test cases

const axios = require('axios');
const ClassA = require('./ClassA');
const ClassB = require('./ClassB');
const ClassC = require('./ClassC');

jest.mock('axios');

describe('ClassC', () => {
let classC;

beforeEach(() => {
// mock process.env to set API_ENDPOINT value to dummy URL
process.env.API_ENDPOINT = 'https://example.com/api';

const classA = new ClassA();
const classB = new ClassB(classA);
classC = new ClassC(classB);

});

afterEach(() => {
jest.resetAllMocks();
// reset process.env to its original state
delete process.env.API_ENDPOINT;
});

it('should retrieve authenticated data from ClassA via ClassB', async () => {
const username = 'testuser';
const password = 'testpassword';
const authToken = 'testauthtoken';
const authenticatedData = { foo: 'bar' };

// mock authenticateUser() method of ClassA to return authToken
jest.spyOn(classC.classBInstance.classAInstance, 'authenticateUser')
  .mockResolvedValue(authToken);

// mock get() method of axios to return authenticatedData
axios.get.mockResolvedValue({ data: authenticatedData });

const result = await classC.getDataFromClassB(username, password);

expect(result).toEqual(authenticatedData);
expect(axios.get).toHaveBeenCalledWith('https://example.com/api', {
  headers: { Authorization: `Bearer ${authToken}` },
});
expect(classC.classBInstance.classAInstance.authenticateUser)
  .toHaveBeenCalledWith(username, password);

});
});

@kendrelaxman
Copy link
Author

it('should wait for Core.loadData to complete if multiple calls are made in quick succession', async () => {
// Arrange
const config1 = { queryToken: 'test1' };
const config2 = { queryToken: 'test1' };
const config3 = { queryToken: 'test2' };
const isRequestFromCache = false;
const data = { prop1: 'value1', prop2: 'value2' };
const callback1 = jest.fn();
const callback2 = jest.fn();
const callback3 = jest.fn();
jest.spyOn(Core, 'loadData').mockImplementationOnce(
() =>
new Promise((resolve) => {
setTimeout(() => resolve(data), 1000);
})
);

  // Act
  const instance = new A();
  instance.loadData(config1, isRequestFromCache, callback1);
  instance.loadData(config2, isRequestFromCache, callback2);
  instance.loadData(config3, isRequestFromCache, callback3);
  await new Promise((resolve) => setTimeout(resolve, 500));
  expect(Core.loadData).toHaveBeenCalledTimes(1);

  await new Promise((resolve) => setTimeout(resolve, 1000));
  expect(Core.loadData).toHaveBeenCalledTimes(1);
  expect(callback1).toHaveBeenCalledWith(data);
  expect(callback2).toHaveBeenCalledWith(data);

  await new Promise((resolve) => setTimeout(resolve, 1000));
  expect(Core.loadData).toHaveBeenCalledTimes(2);
  expect(callback3).toHaveBeenCalledWith(data);
});

@kendrelaxman
Copy link
Author

{
"gridLayout": {
"rows": 3,
"columns": 4,
"fields": [
{
"row": 1,
"column": 2,
"type": "Field",
"label": "Field 1",
"defaultValue": ""
},
{
"row": 2,
"column": 3,
"type": "Label",
"label": "Label 1",
"defaultValue": "Default Value 1"
},
{
"row": 3,
"column": 1,
"type": "Field",
"label": "Field 2",
"defaultValue": ""
},
{
"row": 1,
"column": 4,
"type": "Label",
"label": "Label 2",
"defaultValue": "Default Value 2"
}
]
}
}

import React from 'react';

function GridLayout(props) {
const { rows, columns, fields } = props.data.gridLayout;

// Create an empty grid layout
const grid = [];
for (let i = 0; i < rows; i++) {
grid[i] = [];
for (let j = 0; j < columns; j++) {
grid[i][j] = null;
}
}

// Place the fields and labels in the grid layout
fields.forEach((field) => {
const { row, column, type, label, defaultValue } = field;
const component = type === 'Field' ? (

) : (
{defaultValue}
);
grid[row - 1][column - 1] = (
<div key={${row}-${column}} className="grid-item">
{component}
{label}

);
});

// Render the grid layout
return (


{grid.map((row, rowIndex) => (

{row.map((item, columnIndex) => (

{item}

))}

))}

);
}

export default GridLayout;

@kendrelaxman
Copy link
Author

{
"panel": {
"columns": [
{
"name": "Column 1",
"rows": [
{
"name": "Row 1",
"components": [
{
"type": "Label",
"label": "Label 1",
"defaultValue": "Default Value 1"
},
{
"type": "InputField",
"label": "Input Field 1",
"defaultValue": ""
}
]
},
{
"name": "Row 2",
"components": [
{
"type": "Dropdown",
"label": "Dropdown Field 1",
"options": ["Option 1", "Option 2", "Option 3"],
"defaultValue": "Option 1"
}
]
}
]
},
{
"name": "Column 2",
"rows": [
{
"name": "Row 1",
"components": [
{
"type": "Label",
"label": "Label 2",
"defaultValue": "Default Value 2"
},
{
"type": "InputField",
"label": "Input Field 2",
"defaultValue": ""
}
]
},
{
"name": "Row 2",
"components": [
{
"type": "Dropdown",
"label": "Dropdown Field 2",
"options": ["Option 1", "Option 2", "Option 3"],
"defaultValue": "Option 1"
}
]
}
]
}
]
}
}

@kendrelaxman
Copy link
Author

.container {
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.group {
border: 1px solid #ccc;
padding: 20px;
}

.fields-container {
display: grid;
gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

.field {
display: grid;
gap: 5px;
grid-template-columns: max-content auto max-content;
align-items: center;
}

.field label {
justify-self: start;
}

.field span {
justify-self: end;
}

import React from 'react';
import './JsonComponent.css'; // Import the CSS file for styling

const JsonComponent = ({ jsonData, dataObject }) => {
// Render the JSON structure
const renderContainer = (container) => {
return (


{container.groups.map((group, index) => (

{group.name}


{renderFields(group.fields)}

))}

);
};

const renderFields = (fields) => {
return (


{fields.map((field, index) => (

{field.label}
{renderControl(field.control)}
{field.endRow}

))}

);
};

const renderControl = (control) => {
const { type, initialValue, dataKey, optionsDataKey } = control[0];

switch (type) {
  case 'input':
    return (
      <input
        type="text"
        value={initialValue}
        onChange={() => {}}
      />
    );
  case 'dropdown':
    const options = dataObject[optionsDataKey] || [];
    return (
      <select value={initialValue} onChange={() => {}}>
        {options.map((option, optionIndex) => (
          <option key={optionIndex} value={option}>
            {option}
          </option>
        ))}
      </select>
    );
  default:
    return null;
}

};

return (


{jsonData.container.map((container, index) => (

{renderContainer(container)}

))}

);
};

export default JsonComponent;

@kendrelaxman
Copy link
Author

const mergeObjects = (target, source, sourceName) => {
for (const key in source) {
if (source.hasOwnProperty(key)) {
const propertyName = ${key}_metadata;
target[propertyName] = sourceName;

  if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
    if (!target.hasOwnProperty(key)) {
      target[key] = {};
    }
    mergeObjects(target[key], source[key], sourceName);
  } else {
    const propertyValue = source[key];
    target[key] = propertyValue;

    if (typeof propertyValue !== 'object' || propertyValue === null || Array.isArray(propertyValue)) {
      target[propertyName] = sourceName;
    }
  }
}

}
};

@kendrelaxman
Copy link
Author

function getProperty(obj: any, propertyString: string): any {
const properties = propertyString.split('.');
return properties.reduce((currentObj, prop) => {
try {
return currentObj[prop];
} catch (error) {
return undefined;
}
}, obj);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment