From 0be77a6f416b80d4043d2795fdac2240db2a26cb Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 9 Aug 2024 03:33:12 +0200 Subject: [PATCH] :sparkles: Connections for BigCommerce & SquareSpace in Ecommerce --- apps/magic-link/src/lib/ProviderModal.tsx | 8 +- .../connections/connections.controller.ts | 15 +- .../crm/services/hubspot/hubspot.service.ts | 2 +- .../ecommerce/ecommerce.connection.module.ts | 4 + .../bigcommerce/bigcommerce.service.ts | 147 +++++++++++ .../services/shopify/shopify.service.ts | 7 +- .../squarespace/squarespace.service.ts | 230 ++++++++++++++++++ packages/api/swagger/swagger-spec.yaml | 116 ++++++++- packages/shared/src/authUrl.ts | 3 +- packages/shared/src/connectors/metadata.ts | 29 ++- 10 files changed, 536 insertions(+), 25 deletions(-) create mode 100644 packages/api/src/@core/connections/ecommerce/services/bigcommerce/bigcommerce.service.ts create mode 100644 packages/api/src/@core/connections/ecommerce/services/squarespace/squarespace.service.ts diff --git a/apps/magic-link/src/lib/ProviderModal.tsx b/apps/magic-link/src/lib/ProviderModal.tsx index 9958cb511..cb2746ab5 100644 --- a/apps/magic-link/src/lib/ProviderModal.tsx +++ b/apps/magic-link/src/lib/ProviderModal.tsx @@ -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 = () => { @@ -349,6 +350,9 @@ const ProviderModal = () => { Enter your credentials + {domainFormats[selectedProvider?.provider.toLowerCase()] && ( + (e.g., {domainFormats[selectedProvider?.provider.toLowerCase()]}) + )} {/*
*/} @@ -357,7 +361,9 @@ const ProviderModal = () => { {selectedProvider.provider!=='' && selectedProvider.category!=='' && CONNECTORS_METADATA[selectedProvider.category][selectedProvider.provider].authStrategy.properties?.map((fieldName : string) => ( <> - + { + 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 { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/api/src/@core/connections/ecommerce/services/shopify/shopify.service.ts b/packages/api/src/@core/connections/ecommerce/services/shopify/shopify.service.ts index 484fb5952..b5686324d 100644 --- a/packages/api/src/@core/connections/ecommerce/services/shopify/shopify.service.ts +++ b/packages/api/src/@core/connections/ecommerce/services/shopify/shopify.service.ts @@ -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 { @@ -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; @@ -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({ diff --git a/packages/api/src/@core/connections/ecommerce/services/squarespace/squarespace.service.ts b/packages/api/src/@core/connections/ecommerce/services/squarespace/squarespace.service.ts new file mode 100644 index 000000000..9d51be370 --- /dev/null +++ b/packages/api/src/@core/connections/ecommerce/services/squarespace/squarespace.service.ts @@ -0,0 +1,230 @@ +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, + OAuth2AuthData, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; +import axios from 'axios'; + +export interface SquarespaceOAuthResponse { + token_type: string; + refresh_token: string; + access_token: string; + access_token_expires_at: string; +} + +@Injectable() +export class SquarespaceConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(SquarespaceConnectionService.name); + this.registry.registerService('squarespace', this); + this.type = providerToType('squarespace', 'ecommerce', AuthStrategy.oauth2); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + 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, + Authorization: `Bearer ${access_token}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'ecommerce.squarespace.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, code } = opts; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'squarespace', + vertical: 'ecommerce', + }, + }); + if (isNotUnique) return; + //reconstruct the redirect URI that was passed in the frontend it must be the same + const REDIRECT_URI = `${this.env.getPanoraBaseUrl()}/connections/oauth/callback`; + + const CREDENTIALS = (await this.cService.getCredentials( + projectId, + this.type, + )) as OAuth2AuthData; + + const formData = new URLSearchParams({ + grant_type: 'authorization_code', + redirect_uri: REDIRECT_URI, + code: code, + }); + const res = await axios.post( + 'https://login.squarespace.com/api/1/login/oauth/provider/tokens', + formData.toString(), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + Authorization: `Basic ${Buffer.from( + `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, + ).toString('base64')}`, + }, + }, + ); + const data: SquarespaceOAuthResponse = res.data; + // save tokens for this customer inside our db + let db_res; + const connection_token = uuidv4(); + + if (isNotUnique) { + // Update existing connection + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(data.access_token), + refresh_token: this.cryptoService.encrypt(data.refresh_token), + expiration_timestamp: new Date( + Number(data.access_token_expires_at) * 1000, + ).toISOString(), + status: 'valid', + created_at: new Date(), + }, + }); + } else { + // Create new connection + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'squarespace', + vertical: 'ecommerce', + token_type: 'oauth2', + account_url: CONNECTORS_METADATA['ecommerce']['squarespace'].urls + .apiUrl as string, + access_token: this.cryptoService.encrypt(data.access_token), + refresh_token: this.cryptoService.encrypt(data.refresh_token), + expiration_timestamp: new Date( + Number(data.access_token_expires_at) * 1000, + ).toISOString(), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + this.logger.log('Successfully added tokens inside DB ' + db_res); + return db_res; + } catch (error) { + throw error; + } + } + + async handleTokenRefresh(opts: RefreshParams) { + try { + const { connectionId, refreshToken, projectId } = opts; + const CREDENTIALS = (await this.cService.getCredentials( + projectId, + this.type, + )) as OAuth2AuthData; + + const formData = new URLSearchParams({ + grant_type: 'refresh_token', + refresh_token: this.cryptoService.decrypt(refreshToken), + }); + + const res = await axios.post( + 'https://login.squarespace.com/api/1/login/oauth/provider/tokens', + formData.toString(), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + Authorization: `Basic ${Buffer.from( + `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, + ).toString('base64')}`, + }, + }, + ); + const data: SquarespaceOAuthResponse = res.data; + const res_ = await this.prisma.connections.update({ + where: { + id_connection: connectionId, + }, + data: { + access_token: this.cryptoService.encrypt(data.access_token), + refresh_token: this.cryptoService.encrypt(data.refresh_token), + expiration_timestamp: new Date( + Number(data.access_token_expires_at) * 1000, + ).toISOString(), + }, + }); + this.logger.log('OAuth credentials updated : squarespace'); + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/swagger/swagger-spec.yaml b/packages/api/swagger/swagger-spec.yaml index bb601fcfc..7fc0ec35f 100644 --- a/packages/api/swagger/swagger-spec.yaml +++ b/packages/api/swagger/swagger-spec.yaml @@ -107,6 +107,8 @@ paths: schema: type: string responses: + '200': + description: '' '201': description: '' content: @@ -127,6 +129,8 @@ paths: schema: type: string responses: + '200': + description: '' '201': description: '' content: @@ -157,6 +161,8 @@ paths: type: object additionalProperties: true description: Dynamic event payload + '201': + description: '' tags: *ref_0 x-speakeasy-group: webhooks /ticketing/tickets: @@ -183,6 +189,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -307,6 +314,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -398,6 +406,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -487,6 +496,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -622,6 +632,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -746,6 +757,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -870,6 +882,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -993,6 +1006,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1117,6 +1131,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1241,6 +1256,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1332,6 +1348,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1455,6 +1472,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1546,6 +1564,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1638,6 +1657,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1765,6 +1785,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -1856,6 +1877,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2155,11 +2177,15 @@ paths: required: false in: query schema: + minimum: 1 + default: 1 type: number - name: limit required: false in: query schema: + minimum: 1 + default: 10 type: number responses: '200': @@ -2195,6 +2221,12 @@ paths: application/json: schema: type: object + '201': + description: '' + content: + application/json: + schema: + type: object tags: &ref_21 - passthrough x-speakeasy-group: passthrough @@ -2240,6 +2272,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2331,6 +2364,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2422,6 +2456,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2513,6 +2548,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2604,6 +2640,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2696,6 +2733,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2819,6 +2857,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -2911,6 +2950,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3002,6 +3042,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3093,6 +3134,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3184,6 +3226,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3275,6 +3318,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3366,6 +3410,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3489,6 +3534,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3580,6 +3626,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3709,6 +3756,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3840,6 +3888,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -3969,6 +4018,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4098,6 +4148,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4192,6 +4243,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4286,6 +4338,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4414,6 +4467,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4508,6 +4562,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4636,6 +4691,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4730,6 +4786,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4854,6 +4911,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -4978,6 +5036,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5102,6 +5161,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5226,6 +5286,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5317,6 +5378,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5441,6 +5503,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5533,6 +5596,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5624,6 +5688,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5715,6 +5780,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5806,6 +5872,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5897,6 +5964,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -5988,6 +6056,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6079,6 +6148,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6170,6 +6240,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6259,6 +6330,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6383,6 +6455,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6474,6 +6547,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6599,6 +6673,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6691,6 +6766,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6783,6 +6859,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6875,6 +6952,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -6999,6 +7077,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7091,6 +7170,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7215,6 +7295,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7307,6 +7388,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7431,6 +7513,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7522,6 +7605,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7647,6 +7731,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7771,6 +7856,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7863,6 +7949,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -7988,6 +8075,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8079,6 +8167,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8171,6 +8260,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8263,6 +8353,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8355,6 +8446,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8446,6 +8538,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8570,6 +8663,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8694,6 +8788,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8785,6 +8880,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8876,6 +8972,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -8998,6 +9095,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9120,6 +9218,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9209,6 +9308,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9299,6 +9399,7 @@ paths: example: 10 description: Set to get the number of records. schema: + default: 50 type: number - name: cursor required: false @@ -9429,7 +9530,6 @@ components: password_hash: type: string required: - - id_user - email - password_hash Connection: @@ -9517,8 +9617,8 @@ components: description: The unique UUID of the webhook. endpoint_description: type: string - example: Webhook to receive connection events nullable: true + example: Webhook to receive connection events description: The description of the webhook. url: type: string @@ -9556,8 +9656,8 @@ components: last_update: format: date-time type: string - example: '2024-10-01T12:00:00Z' nullable: true + example: '2024-10-01T12:00:00Z' description: The last update date of the webhook. required: - id_webhook_endpoint @@ -9592,7 +9692,6 @@ components: type: string required: - url - - description - scope SignatureVerificationDto: type: object @@ -11725,8 +11824,6 @@ components: - id_project - name - sync_mode - - pull_frequency - - redirect_url - id_user - id_connector_set CreateProjectDto: @@ -12095,10 +12192,10 @@ components: type: object properties: method: - type: string enum: - GET - POST + type: string path: type: string nullable: true @@ -12117,12 +12214,11 @@ components: type: object additionalProperties: true nullable: true + headers: + type: object required: - method - path - - data - - request_format - - overrideBaseUrl UnifiedHrisBankinfoOutput: type: object properties: {} diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index 3021d0eb7..743499225 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -162,7 +162,7 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { if(providerName === 'helpscout') { params = `client_id=${encodeURIComponent(clientId)}&state=${state}`; } - if(providerName === 'pipedrive' || providerName === 'shopify') { + if(providerName === 'pipedrive' || providerName === 'shopify' || providerName === 'squarespace') { params = `client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodedRedirectUrl}&state=${state}`; } @@ -187,6 +187,7 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { // Special cases for certain providers switch (providerName) { case 'zoho': + case 'squarespace': params += '&access_type=offline'; break; case 'jira': diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 0184a7d1d..4bc9ef51c 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -2794,8 +2794,35 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, }, 'ecommerce': { + 'bigcommerce': { + urls: { + docsUrl: 'https://developer.bigcommerce.com/docs/rest-catalog', + apiUrl: (storeHash) => `https://api.bigcommerce.com/stores/${storeHash}`, + }, + logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTH_-bQ399xl-yfJYhbLraU-w0yWBcppLf8NA&s', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: true, + authStrategy: { + strategy: AuthStrategy.api_key, + properties: ['api_key', 'store_hash'] + } + }, + 'squarespace': { + scopes: 'website.orders,website.orders.read,website.inventory,website.inventory.read,website.products,website.products.read', + urls: { + docsUrl: 'https://developers.squarespace.com/commerce-apis/overview', + apiUrl: `https://api.squarespace.com`, + authBaseUrl: 'https://login.squarespace.com/api/1/login/oauth/provider/authorize' + }, + logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTu9U-j_3EMYlKtu5dRaTl6ejitL2X6lz3pYg&s', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: true, + authStrategy: { + strategy: AuthStrategy.oauth2, + } + }, 'shopify': { - scopes: 'read_all_orders,read_assigned_fulfillment_orders,read_customers,read_fulfillments,read_orders,write_orders,read_products,write_products', + //scopes: 'read_all_orders,read_assigned_fulfillment_orders,read_customers,read_fulfillments,read_orders,write_orders,read_products,write_products', urls: { docsUrl: 'https://shopify.dev/docs/apps/build', apiUrl: (storeName: string) => `https://${storeName}.myshopify.com`,