Skip to content

Commit

Permalink
✨ Connections for BigCommerce & SquareSpace in Ecommerce
Browse files Browse the repository at this point in the history
  • Loading branch information
naelob committed Aug 9, 2024
1 parent 42b3b49 commit 0be77a6
Show file tree
Hide file tree
Showing 10 changed files with 536 additions and 25 deletions.
8 changes: 7 additions & 1 deletion apps/magic-link/src/lib/ProviderModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface IBasicAuthFormData {

const domainFormats: { [key: string]: string } = {
microsoftdynamicssales: 'YOURORGNAME.api.crm12.dynamics.com',
bigcommerce: 'If your api domain is https://api.bigcommerce.com/stores/eubckcvkzg/v3 then store_hash is eubckcvkzg'
};

const ProviderModal = () => {
Expand Down Expand Up @@ -349,6 +350,9 @@ const ProviderModal = () => {
<DialogContent>
<DialogHeader>
<DialogTitle>Enter your credentials</DialogTitle>
{domainFormats[selectedProvider?.provider.toLowerCase()] && (
<span className="text-sm text-gray-500"> (e.g., {domainFormats[selectedProvider?.provider.toLowerCase()]})</span>
)}
</DialogHeader>
{/* <Form {...form}> */}
<form onSubmit={handleSubmit2(onBasicAuthSubmit)}>
Expand All @@ -357,7 +361,9 @@ const ProviderModal = () => {
{selectedProvider.provider!=='' && selectedProvider.category!=='' && CONNECTORS_METADATA[selectedProvider.category][selectedProvider.provider].authStrategy.properties?.map((fieldName : string) =>
(
<>
<Label className={errors2[fieldName] ? 'text-destructive' : ''}>Enter your {fieldName} for {selectedProvider?.provider.substring(0,1).toUpperCase()}{selectedProvider?.provider.substring(1)}</Label>
<Label className={errors2[fieldName] ? 'text-destructive' : ''}>
Enter your {fieldName} for {selectedProvider?.provider.substring(0,1).toUpperCase()}{selectedProvider?.provider.substring(1)}
</Label>
<Input
type='text'
placeholder={`Your ${fieldName}`}
Expand Down
15 changes: 9 additions & 6 deletions packages/api/src/@core/connections/connections.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ export class ConnectionsController {
}
const stateData: StateDataType = JSON.parse(decodeURIComponent(state));
const { projectId, vertical, linkedUserId, providerName } = stateData;

const strategy =
CONNECTORS_METADATA[vertical.toLowerCase()][providerName.toLowerCase()]
.authStrategy.strategy;
Expand All @@ -178,11 +177,15 @@ export class ConnectionsController {
const service = this.categoryConnectionRegistry.getService(
vertical.toLowerCase(),
);
await service.handleCallBack(providerName, {
projectId,
linkedUserId,
body,
}, strategy_type);
await service.handleCallBack(
providerName,
{
projectId,
linkedUserId,
body,
},
strategy_type,
);
/*if (
CONNECTORS_METADATA[vertical.toLowerCase()][providerName.toLowerCase()]
.active !== false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export class HubspotConnectionService extends AbstractBaseConnectionService {

const queryString = new URLSearchParams(params).toString();
const url = `https://api.hubapi.com/oauth/v1/token?${queryString}`;
const res = await axios.post(url, null, {
const res = await axios.post(url, queryString, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { EcommerceConnectionsService } from './services/ecommerce.connection.ser
import { ServiceRegistry } from './services/registry.service';
import { ShopifyConnectionService } from './services/shopify/shopify.service';
import { WoocommerceConnectionService } from './services/woocommerce/woocommerce.service';
import { SquarespaceConnectionService } from './services/squarespace/squarespace.service';
import { BigcommerceConnectionService } from './services/bigcommerce/bigcommerce.service';

@Module({
imports: [WebhookModule, BullQueueModule],
Expand All @@ -20,6 +22,8 @@ import { WoocommerceConnectionService } from './services/woocommerce/woocommerce
//PROVIDERS SERVICES,
ShopifyConnectionService,
WoocommerceConnectionService,
SquarespaceConnectionService,
BigcommerceConnectionService,
],
exports: [EcommerceConnectionsService],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { EncryptionService } from '@@core/@core-services/encryption/encryption.service';
import { LoggerService } from '@@core/@core-services/logger/logger.service';
import { PrismaService } from '@@core/@core-services/prisma/prisma.service';
import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler';
import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service';
import { ConnectionUtils } from '@@core/connections/@utils';
import {
AbstractBaseConnectionService,
OAuthCallbackParams,
PassthroughInput,
RefreshParams,
} from '@@core/connections/@utils/types';
import { PassthroughResponse } from '@@core/passthrough/types';
import { Injectable } from '@nestjs/common';
import {
AuthStrategy,
CONNECTORS_METADATA,
DynamicApiUrl,
providerToType,
} from '@panora/shared';
import { v4 as uuidv4 } from 'uuid';
import { ServiceRegistry } from '../registry.service';

@Injectable()
export class BigcommerceConnectionService extends AbstractBaseConnectionService {
private readonly type: string;

constructor(
protected prisma: PrismaService,
private logger: LoggerService,
protected cryptoService: EncryptionService,
private registry: ServiceRegistry,
private connectionUtils: ConnectionUtils,
private cService: ConnectionsStrategiesService,
private retryService: RetryHandler,
) {
super(prisma, cryptoService);
this.logger.setContext(BigcommerceConnectionService.name);
this.registry.registerService('bigcommerce', this);
this.type = providerToType('bigcommerce', 'ecommerce', AuthStrategy.oauth2);
}

async passthrough(
input: PassthroughInput,
connectionId: string,
): Promise<PassthroughResponse> {
try {
const { headers } = input;
const config = await this.constructPassthrough(input, connectionId);

const connection = await this.prisma.connections.findUnique({
where: {
id_connection: connectionId,
},
});

const access_token = JSON.parse(
this.cryptoService.decrypt(connection.access_token),
);
config.headers = {
...config.headers,
...headers,
'X-Auth-Token': access_token,
};

return await this.retryService.makeRequest(
{
method: config.method,
url: config.url,
data: config.data,
headers: config.headers,
},
'ecommerce.bigcommerce.passthrough',
config.linkedUserId,
);
} catch (error) {
throw error;
}
}

async handleCallback(opts: OAuthCallbackParams) {
try {
const { linkedUserId, projectId, body } = opts;
const { api_key, store_hash } = body;
const isNotUnique = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'bigcommerce',
vertical: 'ecommerce',
},
});

let db_res;
const connection_token = uuidv4();
const BASE_API_URL = (
CONNECTORS_METADATA['ecommerce']['bigcommerce'].urls
.apiUrl as DynamicApiUrl
)(store_hash);

if (isNotUnique) {
db_res = await this.prisma.connections.update({
where: {
id_connection: isNotUnique.id_connection,
},
data: {
access_token: this.cryptoService.encrypt(api_key),
account_url: BASE_API_URL,
status: 'valid',
created_at: new Date(),
},
});
} else {
db_res = await this.prisma.connections.create({
data: {
id_connection: uuidv4(),
connection_token: connection_token,
provider_slug: 'bigcommerce',
vertical: 'ecommerce',
token_type: 'basic',
account_url: BASE_API_URL,
access_token: this.cryptoService.encrypt(api_key),
status: 'valid',
created_at: new Date(),
projects: {
connect: { id_project: projectId },
},
linked_users: {
connect: {
id_linked_user: await this.connectionUtils.getLinkedUserId(
projectId,
linkedUserId,
),
},
},
},
});
}
return db_res;
} catch (error) {
throw error;
}
}

handleTokenRefresh?(opts: RefreshParams): Promise<any> {
throw new Error('Method not implemented.');
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EncryptionService } from '@@core/@core-services/encryption/encryption.service';
import { LoggerService } from '@@core/@core-services/logger/logger.service';
import { PrismaService } from '@@core/@core-services/prisma/prisma.service';
import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler';
import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service';
import { ConnectionUtils } from '@@core/connections/@utils';
import {
Expand All @@ -15,13 +16,10 @@ import {
AuthStrategy,
CONNECTORS_METADATA,
DynamicApiUrl,
OAuth2AuthData,
providerToType,
} from '@panora/shared';
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
import { ServiceRegistry } from '../registry.service';
import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler';

export type ShopifyOAuthResponse = {
access_token: string;
Expand Down Expand Up @@ -104,8 +102,7 @@ export class ShopifyConnectionService extends AbstractBaseConnectionService {
let db_res;
const connection_token = uuidv4();
const BASE_API_URL = (
CONNECTORS_METADATA['ecommerce']['shopify'].urls
.apiUrl as DynamicApiUrl
CONNECTORS_METADATA['ecommerce']['shopify'].urls.apiUrl as DynamicApiUrl
)(store_url);
if (isNotUnique) {
db_res = await this.prisma.connections.update({
Expand Down
Loading

0 comments on commit 0be77a6

Please sign in to comment.