Basic plugin structure
The following is the basic structure of all Agry plugins; please adhere to best practices to develop plugins that are functional and compliant for publication.
(async () => {
const EXTERNAL_URL = 'https://jsonplaceholder.typicode.com/todos/1';
const POST_URL = 'https://jsonplaceholder.typicode.com/posts';
const setResult = async (message, type = 'info') => {
await Agry.log(message, type);
await Agry.dom.setHTML(
'sdk-test-result',
`<strong>Status:</strong> ${message}`
);
};
// Interface: Ribbon button and side widget.
await Agry.addRibbonButton('SDK Test', '🧪', async () => {
await Agry.toast('SDK test plugin active.', 'success');
});
await Agry.addWidget(
'sdk-test-widget',
'SDK Diagnostics',
'🧪',
'<p>Test plugin loaded successfully.</p><small>Open the diagnostic window from the Ribbon button.</small>'
);
// Interface: plugin window.
await Agry.createWindow(
'sdk-test-window',
'Agry SDK Diagnostics',
`
<p>This plugin tests capabilities without modifying the logbook, planner, or inventory.</p>
<div id="sdk-test-result">
<strong>Status:</strong> ready for tests.
</div>
<p>
<button id="sdk-test-storage">Storage test</button>
<button id="sdk-test-network">External GET + POST</button>
</p>
<p>
<button id="sdk-test-project">Read Agry fields</button>
<button id="sdk-test-account">Read account profile</button>
</p>
<p>
<button id="sdk-test-devices">Read devices</button>
<button id="sdk-test-map">Position + marker</button>
</p>
<p>
<button id="sdk-test-dialog">Test confirmation window</button>
<button id="sdk-test-download">Download report</button>
</p>
`,
480,
'auto'
);
// Isolated storage for this plugin only.
const startedAt = new Date().toISOString();
await Agry.storage.set('last-sdk-test', { startedAt });
const savedTest = await Agry.storage.get('last-sdk-test');
await Agry.log(`Initial storage OK: ${savedTest ? 'data saved' : 'data not found'}`, 'success');
// Window events: no inline onclick, everything passes through the protected API.
await Agry.events.add('click', async (event) => {
const targetId = Agry.events.getTargetId(event);
try {
if (targetId === 'sdk-test-storage') {
const keys = await Agry.storage.getAllKeys();
await setResult(`Storage OK: ${keys.length} keys available.`, 'success');
}
if (targetId === 'sdk-test-network') {
await setResult('External GET request in progress…');
// On the first run, permission for the domain will be requested.
const getData = await Agry.httpGet(EXTERNAL_URL);
await Agry.log(`External GET OK: ${JSON.stringify(getData)}`, 'success');
await setResult('External POST request in progress…');
const postData = await Agry.httpPost(POST_URL, {
title: 'Agry SDK Test',
completed: false,
source: 'plugin-studio'
});
await Agry.log(`External POST OK: ${JSON.stringify(postData)}`, 'success');
await setResult('Network OK: GET and POST completed.', 'success');
await Agry.toast('Network test passed.', 'success');
}
if (targetId === 'sdk-test-project') {
const fields = await Agry.fields.list();
await Agry.log(`Fields read: ${fields.length}`, 'success');
await setResult(`Project data OK: found ${fields.length} fields.`, 'success');
}
if (targetId === 'sdk-test-account') {
const profile = await MyMicroeden.account.getProfile();
await Agry.log('Account profile read successfully.', 'success');
// Does not show personal data in the UI: only verifies the response.
await setResult(
profile && profile.success !== false
? 'Account OK: profile available.'
: 'Profile received, but no data available.',
'success'
);
}
if (targetId === 'sdk-test-devices') {
const devices = await MyMicroeden.devices.list();
const count = Array.isArray(devices?.data)
? devices.data.length
: Array.isArray(devices)
? devices.length
: 0;
await Agry.log(`Devices read: ${count}`, 'success');
await setResult(`Devices OK: ${count} devices detected.`, 'success');
}
if (targetId === 'sdk-test-map') {
await setResult('Position request in progress…');
const position = await Agry.map.getUserPosition({
centerMap: false,
addMarker: false
});
await Agry.addMarker(
position.lat,
position.lng,
'Marker created by the SDK test',
'🧪',
'normal'
);
await setResult('Map OK: position read and marker created.', 'success');
await Agry.toast('Test marker added.', 'success');
}
if (targetId === 'sdk-test-dialog') {
await Agry.ui.confirm(
'The Agry windows test works. Confirm?',
async () => {
await setResult('Confirmation window OK.', 'success');
await Agry.toast('Confirmation received.', 'success');
}
);
}
if (targetId === 'sdk-test-download') {
const report = {
plugin: 'SDK Test',
executedAt: new Date().toISOString(),
storage: savedTest ? 'ok' : 'not available'
};
await Agry.file.download(
JSON.stringify(report, null, 2),
'agry-sdk-test-report.json',
'application/json'
);
await setResult('Download OK: report generated.', 'success');
}
} catch (error) {
await Agry.log(`Test failed: ${error.message}`, 'error');
await setResult(`Error: ${error.message}`, 'error');
await Agry.toast('A test requires authorization or has failed.', 'error');
}
});
await Agry.onStop(async () => {
await Agry.log('Test plugin stopped: plugin resources removed.', 'info');
});
await Agry.toast('Diagnostic plugin ready.', 'success');
await Agry.log('SDK Diagnostics ready. Use the window to test capabilities.', 'success');
Agry.addWidget('id', 'Title', '📊', '<div>Content</div>');
})();