Skip to content

Commit

Permalink
Merge pull request #1 from DavidDurman/first-commit
Browse files Browse the repository at this point in the history
First commit
  • Loading branch information
DavidDurman authored Feb 9, 2024
2 parents 85fc894 + aa5f886 commit 1371a4a
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projects": {
"default": "am-demo-embedded-integrations"
}
}
21 changes: 21 additions & 0 deletions .github/workflows/firebase-hosting-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file was auto-generated by the Firebase CLI
# https://github.com/firebase/firebase-tools

name: Deploy to Firebase Hosting on merge
'on':
push:
branches:
- main
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: >-
${{ secrets.FIREBASE_SERVICE_ACCOUNT_AM_DEMO_EMBEDDED_INTEGRATIONS
}}
channelId: live
projectId: am-demo-embedded-integrations
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Firebase cache
.firebase/

16 changes: 16 additions & 0 deletions firebase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hosting": {
"public": ".",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
1 change: 1 addition & 0 deletions github-mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 75 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Appmixer Embed Integrations Demo</title>
<link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body>
<div class="container">
<div class="guide">
<img id="yoursaas" src="./yoursaas.svg" />

<h1>Appmixer Embedded Integrations Demo</h1>
<p>This demo shows you how to embed Appmixer Integrations in your own web application.</p>

<h2>1. Try the Appmixer Integrations UI</h2>
<p>The Appmixer Integrations UI allows end-users of your application to browse and activate integrations that you pre-built for them in the Appmixer Studio.</p>
<p><b>Try it yourself and activate an integration by clicking on the integration tile and configuring the integration.</b></p>
</p>
<p class="small">Note that this integration will run in the context of an Appmixer virtual user that was created for you in the background when you opened this page.</p>
<p class="small">The Integration marketplace is rendered in the page natively, using the Appmixer JavaScript SDK. It is not included in an iframe.</p>

<h2>2. Trigger the integration by sending an App Event</h2>
<p>Once you have activated an integration, you can trigger it by sending an App Event to Appmixer (assuming your integration starts with the <code>OnAppEvent</code> trigger). This is normally done using the Appmixer JavaScript SDK or an HTTP request:</p>
<pre>
<code>
appmixer.api.sendAppEvent('contact-created', { first: 'David', last: 'Doe' });
</code>
or:
<code>
curl -XPOST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer VIRTUAL_USER_ACCESS_TOKEN" \
-d '{ "first": "David", "last": "Doe" }' \
"https://APPMIXER_TENANT_API_URL/plugins/appmixer/utils/appevents/events/contact-created"
</code>
</pre>
<p><b>In this demo, you can send an App Event using the helper form below. This is to simulate a real-world scenario where you would send an App Event from your own application code.</b></p>

<form class="app-event-form">
<label>App Event name</label><input class="app-event-event" type="text" value="contact-created" placeholder="contact-created" />
<label>App Event data (JSON)</label><textarea class="app-event-data" placeholder="JSON data">{ "first": "David", "last": "Doe" }</textarea>
<input type="submit" value="Send App Event" />
</form>

<a id="github-link" href="https://github.com/clientIO/appmixer-demo-embedded-integrations" target="_blank"><img src="./github-mark.svg" /></a>
</div>
<div class="app">
<div id="appmixer-integrations-marketplace"></div>
<div id="appmixer-integrations-logs"></div>
</div>
</div>

<script>
function loadScript(url, callback) {
const script = document.createElement('script');
script.src = url;
script.onload = callback;
script.onerror = console.error;
document.head.appendChild(script);
}

// The Appmixer SDK is located at https://my.YOUR_TENANT.appmixer.cloud/appmixer/appmixer.js.
const APPMIXER_STUDIO_URL = (new URLSearchParams(window.location.search)).get('studioUrl');

loadScript(APPMIXER_STUDIO_URL + '/appmixer/appmixer.js', () => {
loadScript('./main.js');
});
</script>
</body>
</html>



108 changes: 108 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const QUERY_PARAMS = new URLSearchParams(window.location.search);

// This should have the pattern https://api.YOUR_TENANT.appmixer.cloud';
const APPMIXER_API_URL = QUERY_PARAMS.get('apiUrl');

let APPMIXER_VIRTUAL_USER_USERNAME = QUERY_PARAMS.get('username');
let APPMIXER_VIRTUAL_USER_TOKEN;
let DOMAIN_FILTER = QUERY_PARAMS.get('domainFilter');

if (APPMIXER_VIRTUAL_USER_USERNAME) {
// If username is provided in the query string of this demo, password must be provided as well.
APPMIXER_VIRTUAL_USER_TOKEN = QUERY_PARAMS.get('token');
} else {
// Hardcode one. Normally, you would generate this and store in your application code and pass it to the Appmixer SDK.
// Note that Appmixer username can be any, even non-existing, email address. We're using a user ID
// together with a fictional domain name. Appmixer does not send anything to these email addresses.
// They are just used as a virtual user credentials pair. Moreover, the email domain
// allows us to easily share integration templates with a specific group of users
// (An alternative to this is to use the user scopes.)
APPMIXER_VIRTUAL_USER_USERNAME = '[email protected]';
APPMIXER_VIRTUAL_USER_TOKEN = '4efaa8ec-ddc5-4852-b6cc-cb3039fe17b1';
}

if (typeof Appmixer === 'undefined') {
alert('Appmixer SDK not loaded. Are you sure you pointed to the right appmixer.js SDK location?');
}

// Appmixer SDK instance.
const appmixer = new Appmixer({
baseUrl: APPMIXER_API_URL,
theme: {
variables: { colors: { surface: '#f5f5f5' } }
}
});

const widgets = {
integrations: null,
wizard: null
};

// Learn more about Appmixer virtual users at https://docs.appmixer.com/appmixer/tutorials/appmixer-virtual-users.
async function ensureAppmixerVirtualUser(username, token) {
let auth;
try {
auth = await appmixer.api.authenticateUser(username, token);
appmixer.set('accessToken', auth.token);
} catch (err) {
if (err.response && err.response.status === 403) {
// Virtual user not yet created in Appmixer. Create one.
try {
auth = await appmixer.api.signupUser(username, token);
appmixer.set('accessToken', auth.token);
} catch (err) {
alert('Something went wrong creating a virtual user. ' + err.message);
}
} else {
alert('Something went wrong authenticating a virtual user.');
}
}
}

function createWidgets() {
// Create Integrations Page widget.
widgets.integrations = appmixer.ui.Integrations({
el: '#appmixer-integrations-marketplace',
options: {
showHeader: true,
customFilter: [{
// Show only integration templates shared with users in this demo app.
...(DOMAIN_FILTER && { 'sharedWith.0.domain': DOMAIN_FILTER }),
type: 'integration'
}, {
userId: appmixer.get('user').id,
templateId: '>0'
//type: 'integration-instance' // Uncomment and remove line above once v6 is released.
}]
}
});
widgets.integrations.on('integration:create', templateId => {
widgets.wizard.close();
widgets.wizard.set('flowId', templateId);
widgets.wizard.open();
});
widgets.integrations.on('integration:edit', integrationId => {
widgets.wizard.close();
widgets.wizard.set('flowId', integrationId);
widgets.wizard.open();
});
widgets.wizard = appmixer.ui.Wizard();
widgets.wizard.on('flow:start-after', () => widgets.integrations.reload());
widgets.wizard.on('flow:remove-after', () => {
widgets.integrations.reload();
widgets.wizard.close();
});
}

async function main() {
await ensureAppmixerVirtualUser(APPMIXER_VIRTUAL_USER_USERNAME, APPMIXER_VIRTUAL_USER_TOKEN);
createWidgets();
widgets.integrations.open();

document.querySelector('.app-event-form').addEventListener('submit', (evt) => {
evt.preventDefault();
appmixer.api.sendAppEvent(document.querySelector('.app-event-event').value, JSON.parse(document.querySelector('.app-event-data').value));
});
}

main();
83 changes: 83 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
body {
font-family: "IBM Plex Sans", "SF Pro Text", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.container {
display: flex;
margin: 20px;
}
.guide {
flex: 2;
padding: 50px;
position: relative;
}
.app {
flex: 2;
position: relative;
border: 2px dashed #F3153C;
}
#yoursaas {
position: absolute;
right: 3px;
top: 10px;
width: 150px;
}
#github-link img {
position: absolute;
left: 50px;
top: 20px;
width: 30px;
}
.guide p {
font-size: 16px;
line-height: 1.5;
max-width: 80%;
}
.guide p.small {
font-size: 14px;
color: #666;
}
.guide p b {
color: #007bff;
}
input, select, textarea {
display: block;
box-sizing: border-box;
width: 100%;
border-radius: 0;
}
.app-event-form {
max-width: 400px;
padding: 20px;
border: 1px solid #f5f5f5;
box-shadow: 2px 8px 14px gray;
}
.app-event-form {
display: flex;
flex-direction: column;
gap: 10px;
}
.app-event-form input,
.app-event-form textarea {
padding: 10px;
border: 2px solid #007bff;
border-radius: 5px;
outline: none;
}
.app-event-form input[type="submit"] {
padding: 10px;
border: none;
border-radius: 5px;
background-color: #007bff;
color: white;
cursor: pointer;
}
.app-event-form input[type="submit"]:hover {
background-color: #0056b3;
}
#appmixer-integrations-marketplace {
position: absolute;
inset: 0;
}
.am-integrations {
padding: 50px;
}
8 changes: 8 additions & 0 deletions yoursaas.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1371a4a

Please sign in to comment.