feat(smartcar): Added get vehicle location integration
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Get Vehicle Location',
|
||||||
|
key: 'getVehicleLocation',
|
||||||
|
description: 'Get the location of a vehicle',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Vehicle',
|
||||||
|
key: 'vehicle',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: true,
|
||||||
|
description: 'The vehicle to get the location of.',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listVehicles',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const { vehicle } = $.step.parameters;
|
||||||
|
const response = await $.http.get(
|
||||||
|
`/vehicles/${vehicle}/location?mode=simulated`
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({ raw: response.data });
|
||||||
|
},
|
||||||
|
});
|
3
packages/backend/src/apps/smartcar/actions/index.ts
Normal file
3
packages/backend/src/apps/smartcar/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import getVehicleLocation from './get-vehicle-location';
|
||||||
|
|
||||||
|
export default [getVehicleLocation];
|
11
packages/backend/src/apps/smartcar/assets/favicon.svg
Normal file
11
packages/backend/src/apps/smartcar/assets/favicon.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<svg width="217" height="60" viewBox="0 0 217 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M30 0C13.4328 0 0 13.4328 0 30C0 46.5672 13.4328 60 30 60C46.5672 60 60 46.5672 60 30C60 13.4328 46.5672 0 30 0ZM30 7.5C39.7625 7.5 48.0107 13.7875 51.123 22.5H8.877C11.9893 13.7875 20.2375 7.5 30 7.5ZM30 33.75C29.2583 33.75 28.5333 33.5301 27.9166 33.118C27.2999 32.706 26.8193 32.1203 26.5355 31.4351C26.2516 30.7498 26.1774 29.9958 26.3221 29.2684C26.4668 28.541 26.8239 27.8728 27.3483 27.3483C27.8728 26.8239 28.541 26.4668 29.2684 26.3221C29.9958 26.1774 30.7498 26.2516 31.4351 26.5355C32.1203 26.8193 32.706 27.2999 33.118 27.9166C33.5301 28.5333 33.75 29.2583 33.75 30C33.7503 30.4925 33.6536 30.9803 33.4652 31.4355C33.2769 31.8906 33.0007 32.3041 32.6524 32.6524C32.3041 33.0007 31.8906 33.2769 31.4355 33.4652C30.9803 33.6536 30.4925 33.7503 30 33.75ZM7.5 30C17.7467 30 26.0448 39.873 26.2206 52.119C15.615 50.3027 7.5 41.1107 7.5 30ZM33.7794 52.119C33.9552 39.873 42.2533 30 52.5 30C52.5 41.1107 44.385 50.3027 33.7794 52.119Z" fill="black"/>
|
||||||
|
<path d="M79.8724 40.5406C78.2018 40.5406 76.7369 40.2739 75.4776 39.7405C74.2182 39.184 73.1131 38.3608 72.1622 37.2709L75.015 34.7665C75.6575 35.5781 76.3771 36.1926 77.1738 36.61C77.9705 37.0274 78.8958 37.2361 79.9495 37.2361C81.9028 37.2361 82.8794 36.4941 82.8794 35.01C82.8794 34.4303 82.7123 33.9781 82.3782 33.6535C82.0698 33.3056 81.5301 33.0737 80.7591 32.9578L78.8701 32.6447C76.7626 32.2969 75.2334 31.7056 74.2825 30.8708C73.3316 30.036 72.8561 28.8186 72.8561 27.2185C72.8561 25.3171 73.4729 23.9025 74.7065 22.975C75.9402 22.0242 77.6878 21.5489 79.9495 21.5489C81.5172 21.5489 82.8665 21.7808 83.9974 22.2445C85.1539 22.7083 86.1562 23.3924 87.0044 24.2968L84.2672 26.7664C83.7532 26.1866 83.1235 25.7229 82.3782 25.375C81.6586 25.0272 80.849 24.8533 79.9495 24.8533C78.1504 24.8533 77.2509 25.5837 77.2509 27.0446C77.2509 27.6475 77.4308 28.1113 77.7906 28.436C78.1504 28.7374 78.7159 28.9461 79.4869 29.0621L81.4144 29.3751C83.5219 29.7229 85.0254 30.3143 85.9249 31.1491C86.8502 31.9607 87.3128 33.1085 87.3128 34.5926C87.3128 36.4709 86.6702 37.9318 85.3852 38.9753C84.1002 40.0188 82.2626 40.5406 79.8724 40.5406Z" fill="black"/>
|
||||||
|
<path d="M91.0992 40.1232V31.0447V21.9663H95.7639V24.9924H96.3036C97.1003 22.6967 98.6809 21.5489 101.045 21.5489C103.564 21.5489 105.196 22.7431 105.941 25.1315H106.404C106.841 23.9025 107.509 22.9982 108.409 22.4185C109.334 21.8387 110.452 21.5489 111.763 21.5489C113.459 21.5489 114.782 22.1054 115.733 23.2185C116.684 24.3083 117.16 25.8736 117.16 27.9142V40.1232H112.495V28.6447C112.495 26.2794 111.557 25.0968 109.681 25.0968C108.781 25.0968 108.01 25.3518 107.368 25.862C106.751 26.349 106.443 27.0446 106.443 27.949V40.1232H101.778V28.6447C101.778 27.3925 101.547 26.4881 101.084 25.9316C100.621 25.375 99.9274 25.0968 99.0022 25.0968C98.1027 25.0968 97.3316 25.3518 96.6891 25.862C96.0723 26.349 95.7639 27.0446 95.7639 27.949V40.1232H91.0992Z" fill="black"/>
|
||||||
|
<path d="M135.115 40.1232C133.034 40.1232 131.941 39.0912 131.838 37.0274H131.376C131.119 38.1405 130.541 39.0101 129.641 39.6362C128.741 40.2391 127.624 40.5406 126.287 40.5406C124.437 40.5406 123.049 40.0768 122.124 39.1492C121.198 38.2217 120.736 36.9231 120.736 35.2535C120.736 33.352 121.391 31.9375 122.702 31.0099C124.013 30.0824 125.902 29.6186 128.369 29.6186H131.491V28.0533C131.491 26.9867 131.234 26.1982 130.72 25.6881C130.232 25.1779 129.41 24.9228 128.253 24.9228C127.302 24.9228 126.506 25.1315 125.863 25.5489C125.22 25.9432 124.655 26.5113 124.167 27.2533L121.43 25.0272C122.175 23.9837 123.113 23.1489 124.244 22.5228C125.4 21.8735 126.865 21.5489 128.639 21.5489C133.65 21.5489 136.156 23.6822 136.156 27.949V36.7492H137.93V40.1232H135.115ZM128.06 37.4448C129.011 37.4448 129.821 37.2014 130.489 36.7144C131.157 36.2042 131.491 35.5086 131.491 34.6274V32.2273H128.677C126.518 32.2273 125.439 32.981 125.439 34.4883V35.2535C125.439 36.0187 125.67 36.5753 126.133 36.9231C126.595 37.2709 127.238 37.4448 128.06 37.4448Z" fill="black"/>
|
||||||
|
<path d="M141.547 40.1232V21.9663H146.212V25.6881H146.751C146.983 24.4823 147.458 23.5663 148.178 22.9402C148.923 22.2909 149.887 21.9663 151.069 21.9663H152.071V26.2794H150.529C149.064 26.2794 147.972 26.4881 147.253 26.9055C146.559 27.3229 146.212 28.0533 146.212 29.0968V40.1232H141.547Z" fill="black"/>
|
||||||
|
<path d="M161.367 40.1232C159.748 40.1232 158.578 39.7985 157.859 39.1492C157.139 38.4999 156.779 37.468 156.779 36.0535V25.3402H154.119V21.9663H155.507C156.175 21.9663 156.625 21.8271 156.856 21.5489C157.113 21.2706 157.242 20.83 157.242 20.2271V17.027H161.444V21.9663H165.068V25.3402H161.444V36.7492H164.759V40.1232H161.367Z" fill="black"/>
|
||||||
|
<path d="M175.672 40.5406C172.948 40.5406 170.866 39.7753 169.427 38.2449C168.013 36.7144 167.307 34.3143 167.307 31.0447C167.307 27.7751 168.013 25.375 169.427 23.8446C170.866 22.3141 172.948 21.5489 175.672 21.5489C177.651 21.5489 179.219 21.9547 180.376 22.7663C181.558 23.5779 182.406 24.691 182.92 26.1055L179.103 27.6359C178.898 26.7548 178.525 26.0939 177.985 25.6533C177.471 25.1895 176.7 24.9576 175.672 24.9576C173.359 24.9576 172.203 26.2098 172.203 28.7142V33.3752C172.203 35.8796 173.359 37.1318 175.672 37.1318C176.752 37.1318 177.574 36.8999 178.14 36.4361C178.705 35.9723 179.129 35.2535 179.412 34.2796L183.036 35.8448C181.853 38.9753 179.399 40.5406 175.672 40.5406Z" fill="black"/>
|
||||||
|
<path d="M199.53 40.1232C197.449 40.1232 196.356 39.0912 196.253 37.0274H195.791C195.534 38.1405 194.956 39.0101 194.056 39.6362C193.157 40.2391 192.039 40.5406 190.702 40.5406C188.852 40.5406 187.464 40.0768 186.539 39.1492C185.613 38.2217 185.151 36.9231 185.151 35.2535C185.151 33.352 185.806 31.9375 187.117 31.0099C188.428 30.0824 190.317 29.6186 192.784 29.6186H195.907V28.0533C195.907 26.9867 195.649 26.1982 195.135 25.6881C194.647 25.1779 193.825 24.9228 192.668 24.9228C191.717 24.9228 190.921 25.1315 190.278 25.5489C189.636 25.9432 189.07 26.5113 188.582 27.2533L185.845 25.0272C186.59 23.9837 187.528 23.1489 188.659 22.5228C189.815 21.8735 191.28 21.5489 193.054 21.5489C198.065 21.5489 200.571 23.6822 200.571 27.949V36.7492H202.345V40.1232H199.53ZM192.475 37.4448C193.426 37.4448 194.236 37.2014 194.904 36.7144C195.572 36.2042 195.907 35.5086 195.907 34.6274V32.2273H193.092C190.933 32.2273 189.854 32.981 189.854 34.4883V35.2535C189.854 36.0187 190.085 36.5753 190.548 36.9231C191.011 37.2709 191.653 37.4448 192.475 37.4448Z" fill="black"/>
|
||||||
|
<path d="M205.962 40.1232V21.9663H210.627V25.6881H211.166C211.398 24.4823 211.873 23.5663 212.593 22.9402C213.338 22.2909 214.302 21.9663 215.484 21.9663H216.486V26.2794H214.944C213.48 26.2794 212.387 26.4881 211.668 26.9055C210.974 27.3229 210.627 28.0533 210.627 29.0968V40.1232H205.962Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.6 KiB |
29
packages/backend/src/apps/smartcar/auth/generate-auth-url.ts
Normal file
29
packages/backend/src/apps/smartcar/auth/generate-auth-url.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||||
|
const scopes = [
|
||||||
|
'read_odometer',
|
||||||
|
'read_vehicle_info',
|
||||||
|
'required:read_location',
|
||||||
|
];
|
||||||
|
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value as string;
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
response_type: 'code',
|
||||||
|
client_id: $.auth.data.clientId as string,
|
||||||
|
scope: scopes.join(' '),
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
mode: 'simulated',
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `https://connect.smartcar.com/oauth/authorize?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
}
|
58
packages/backend/src/apps/smartcar/auth/index.ts
Normal file
58
packages/backend/src/apps/smartcar/auth/index.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import generateAuthUrl from './generate-auth-url';
|
||||||
|
import verifyCredentials from './verify-credentials';
|
||||||
|
import isStillVerified from './is-still-verified';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'oAuthRedirectUrl',
|
||||||
|
label: 'OAuth Redirect URL',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
value: '{WEB_APP_URL}/app/twitter/connections/add',
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'When asked to input an OAuth callback or redirect URL in Smartcar OAuth, enter the URL above.',
|
||||||
|
clickToCopy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'screenName',
|
||||||
|
label: 'Screen Name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'Screen name of your connection to be used on Automatisch UI.',
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientId',
|
||||||
|
label: 'Client ID',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientSecret',
|
||||||
|
label: 'Client Secret',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
generateAuthUrl,
|
||||||
|
verifyCredentials,
|
||||||
|
isStillVerified,
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
import getCurrentUser from '../common/get-current-user';
|
||||||
|
|
||||||
|
const isStillVerified = async ($: IGlobalVariable) => {
|
||||||
|
const user = await getCurrentUser($);
|
||||||
|
return !!user;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
@@ -0,0 +1,41 @@
|
|||||||
|
import { IGlobalVariable, IField } from '@automatisch/types';
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||||
|
const token = Buffer.from(
|
||||||
|
`${$.auth.data.clientId}:${$.auth.data.clientSecret}`
|
||||||
|
).toString('base64');
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Basic ${token}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value as string;
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
code: $.auth.data.code as string,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await $.http.post(
|
||||||
|
`https://auth.smartcar.com/oauth/token`,
|
||||||
|
params.toString(),
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData = Object.fromEntries(new URLSearchParams(response.data));
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken: responseData.access_token,
|
||||||
|
tokenType: responseData.token_type,
|
||||||
|
expiresIn: responseData.expires_in,
|
||||||
|
refreshToken: responseData.refresh_token,
|
||||||
|
screenName: $.auth.data.screenName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
13
packages/backend/src/apps/smartcar/common/add-auth-header.ts
Normal file
13
packages/backend/src/apps/smartcar/common/add-auth-header.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { TBeforeRequest } from '@automatisch/types';
|
||||||
|
|
||||||
|
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||||
|
const { accessToken } = $.auth.data;
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
requestConfig.headers.Authorization = `Bearer ${accessToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthHeader;
|
@@ -0,0 +1,10 @@
|
|||||||
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||||
|
const response = await $.http.get('/user');
|
||||||
|
const currentUser = response.data;
|
||||||
|
|
||||||
|
return currentUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentUser;
|
3
packages/backend/src/apps/smartcar/dynamic-data/index.ts
Normal file
3
packages/backend/src/apps/smartcar/dynamic-data/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import listVehicles from './list-vehicles';
|
||||||
|
|
||||||
|
export default [listVehicles];
|
@@ -0,0 +1,31 @@
|
|||||||
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'List vehicles',
|
||||||
|
key: 'listVehicles',
|
||||||
|
|
||||||
|
async run($: IGlobalVariable) {
|
||||||
|
const vehicles: {
|
||||||
|
data: IJSONObject[];
|
||||||
|
error: IJSONObject | null;
|
||||||
|
} = {
|
||||||
|
data: [],
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response: any = await $.http.get('/vehicles');
|
||||||
|
|
||||||
|
for (const vehicle of response.data.vehicles) {
|
||||||
|
const response: any = await $.http.get(`/vehicles/${vehicle}`);
|
||||||
|
|
||||||
|
const vehicleName = `${response.data.make} - ${response.data.model} (${response.data.year})`;
|
||||||
|
|
||||||
|
vehicles.data.push({
|
||||||
|
value: vehicle as string,
|
||||||
|
name: vehicleName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return vehicles;
|
||||||
|
},
|
||||||
|
};
|
0
packages/backend/src/apps/smartcar/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/smartcar/index.d.ts
vendored
Normal file
20
packages/backend/src/apps/smartcar/index.ts
Normal file
20
packages/backend/src/apps/smartcar/index.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import defineApp from '../../helpers/define-app';
|
||||||
|
import addAuthHeader from './common/add-auth-header';
|
||||||
|
import auth from './auth';
|
||||||
|
import actions from './actions';
|
||||||
|
import dynamicData from './dynamic-data';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'Smartcar',
|
||||||
|
key: 'smartcar',
|
||||||
|
iconUrl: '{BASE_URL}/apps/smartcar/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/smartcar/connection',
|
||||||
|
supportsConnections: true,
|
||||||
|
baseUrl: 'https://smartcar.com',
|
||||||
|
apiBaseUrl: 'https://api.smartcar.com/v2.0',
|
||||||
|
primaryColor: '000000',
|
||||||
|
beforeRequest: [addAuthHeader],
|
||||||
|
auth,
|
||||||
|
actions,
|
||||||
|
dynamicData,
|
||||||
|
});
|
Reference in New Issue
Block a user