Skip to content

Commit

Permalink
Merge branch 'main' into feat-sharepoint-integration
Browse files Browse the repository at this point in the history
  • Loading branch information
amuwal committed Sep 11, 2024
2 parents 5ad0523 + f791257 commit 091e72f
Show file tree
Hide file tree
Showing 43 changed files with 2,604 additions and 253 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ LINEAR_TICKETING_CLOUD_CLIENT_SECRET=
# Box
BOX_FILESTORAGE_CLOUD_CLIENT_ID=
BOX_FILESTORAGE_CLOUD_CLIENT_SECRET=
# Onedrive
ONEDRIVE_FILESTORAGE_CLOUD_CLIENT_ID=
ONEDRIVE_FILESTORAGE_CLOUD_CLIENT_SECRET=


# ================================================
Expand Down
4 changes: 1 addition & 3 deletions apps/magic-link/src/hooks/useOAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ type UseOAuthProps = {
value: string | null;
},
onSuccess: () => void;
additionalParams?: {
end_user_domain: string;
}
additionalParams?: {[key: string]: any}
};

const useOAuth = ({ providerName, vertical, returnUrl, projectId, linkedUserId, additionalParams, redirectIngressUri, onSuccess }: UseOAuthProps) => {
Expand Down
161 changes: 50 additions & 111 deletions apps/magic-link/src/lib/ProviderModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ interface IBasicAuthFormData {
}

const domainFormats: { [key: string]: string } = {
microsoftdynamicssales: '{YOUR_DOMAIN}.api.crm3.dynamics.com',
bigcommerce: 'If your api domain is https://api.bigcommerce.com/stores/eubckcvkzg/v3 then store_hash is eubckcvkzg',
salesforce: 'If your Salesforce site URL is https://acme-dev.lightning.force.com, acme-dev is your domain',
sharepoint: 'If the SharePoint site URL is https://joedoe.sharepoint.com/sites/acme-dev, joedoe is the tenant and acme-dev is the site name.',
microsoftdynamicssales: 'If your Microsoft Dynamics URL is acme-dev.api.crm3.dynamics.com then acme-dev is the organization name.',
bigcommerce: 'If your api domain is https://api.bigcommerce.com/stores/joehash123/v3 then store_hash is joehash123.',
};

const ProviderModal = () => {
Expand All @@ -35,18 +37,16 @@ const ProviderModal = () => {
const [startFlow, setStartFlow] = useState<boolean>(false);
const [preStartFlow, setPreStartFlow] = useState<boolean>(false);
const [openBasicAuthDialog,setOpenBasicAuthDialog] = useState<boolean>(false);
const [openDomainDialog, setOpenDomainDialog] = useState<boolean>(false);
const [projectId, setProjectId] = useState<string>("");
const [data, setData] = useState<Provider[]>([]);
const [isProjectIdReady, setIsProjectIdReady] = useState(false);
const [errorResponse,setErrorResponse] = useState<{
errorPresent: boolean; errorMessage : string
}>({errorPresent:false,errorMessage:''})
const [endUserDomain, setEndUserDomain] = useState<string>('');
const [loading, setLoading] = useState<{
status: boolean; provider: string
}>({status: false, provider: ''});

const [additionalParams, setAdditionalParams] = useState<{[key: string]: string}>({});
const [uniqueMagicLinkId, setUniqueMagicLinkId] = useState<string | null>(null);
const [openSuccessDialog,setOpenSuccessDialog] = useState<boolean>(false);
const [currentProviderLogoURL,setCurrentProviderLogoURL] = useState<string>('')
Expand Down Expand Up @@ -121,9 +121,7 @@ const ProviderModal = () => {
console.log('OAuth successful');
setOpenSuccessDialog(true);
},
additionalParams: {
end_user_domain: endUserDomain
}
additionalParams
});

const onWindowClose = () => {
Expand Down Expand Up @@ -196,50 +194,49 @@ const ProviderModal = () => {
setLoading({status: true, provider: selectedProvider?.provider!});
setPreStartFlow(false);
// Creating Basic Auth Connection
createApiKeyConnection({
query : {
linkedUserId: magicLink?.id_linked_user as string,
projectId: projectId,
providerName: selectedProvider?.provider!,
vertical: selectedProvider?.category!
const providerMetadata = CONNECTORS_METADATA[selectedProvider.category][selectedProvider.provider];

if (providerMetadata.authStrategy.strategy === AuthStrategy.oauth2) {
console.log("values are "+ JSON.stringify(values))
setAdditionalParams(values);
setStartFlow(true);
}else{
createApiKeyConnection({
query : {
linkedUserId: magicLink?.id_linked_user as string,
projectId: projectId,
providerName: selectedProvider?.provider!,
vertical: selectedProvider?.category!
},
data: values
},
data: values
},
{
onSuccess: () => {
setSelectedProvider({
provider: '',
category: ''
});

setLoading({
{
onSuccess: () => {
setSelectedProvider({
provider: '',
category: ''
});

setLoading({
status: false,
provider: ''
});
setOpenSuccessDialog(true);
},
onError: (error) => {
setErrorResponse({errorPresent:true,errorMessage: error.message});
setLoading({
status: false,
provider: ''
});
setOpenSuccessDialog(true);
},
onError: (error) => {
setErrorResponse({errorPresent:true,errorMessage: error.message});
setLoading({
status: false,
provider: ''
});
setSelectedProvider({
provider: '',
category: ''
});
}
});
}

const onCloseDomainDialog = (dialogState: boolean) => {
setOpenDomainDialog(dialogState);
}

const onDomainSubmit = () => {
setOpenDomainDialog(false);
setLoading({ status: true, provider: selectedProvider?.provider! });
setStartFlow(true);
});
setSelectedProvider({
provider: '',
category: ''
});
}
});
}

}

const filteredProviders = data.filter(provider =>
Expand All @@ -254,10 +251,8 @@ const ProviderModal = () => {
setCurrentProvider(provider.name.toLowerCase())

const providerMetadata = CONNECTORS_METADATA[provider.vertical!.toLowerCase()][provider.name.toLowerCase()];
if (providerMetadata.authStrategy.strategy === AuthStrategy.api_key || providerMetadata.authStrategy.strategy === AuthStrategy.basic) {
if (providerMetadata.authStrategy.strategy === AuthStrategy.api_key || providerMetadata.authStrategy.strategy === AuthStrategy.basic || (providerMetadata.authStrategy.strategy === AuthStrategy.oauth2 && providerMetadata.authStrategy.properties)) {
setOpenBasicAuthDialog(true);
} else if (providerMetadata?.options?.end_user_domain) {
setOpenDomainDialog(true);
} else {
setLoading({ status: true, provider: provider.name.toLowerCase() });
setStartFlow(true);
Expand Down Expand Up @@ -382,68 +377,12 @@ const ProviderModal = () => {
{errors2[fieldName] && <p className='text-sm font-medium text-red-500'>{errors2[fieldName]?.message}</p>}
</div>
))}

<p className="text-sm text-gray-500 text-center mt-2">
A third-party accountant will be added.
</p>

<Button
type='submit'
className="w-full p-2 rounded-md text-white font-semibold mt-4"
style={{backgroundColor: selectedProvider.provider !== '' && selectedProvider.category !== '' ? CONNECTORS_METADATA[selectedProvider.category][selectedProvider.provider].primaryColor : "#00000000"}}
>
Connect
</Button>
</form>
</div>
</DialogContent>
</Dialog>

{/* Domain Dialog */}
<Dialog open={openDomainDialog} onOpenChange={onCloseDomainDialog}>
<DialogContent className="bg-white text-black rounded-lg shadow-lg max-w-md w-full p-0 overflow-hidden">
<div className="flex justify-between items-center p-4">
<button onClick={() => onCloseDomainDialog(false)} className="text-gray-500 hover:text-gray-700">
<ArrowLeft size={20} />
</button>
</div>

<div className="flex flex-col items-center px-6 pb-6">
{selectedProvider?.category && selectedProvider?.provider && CONNECTORS_METADATA[selectedProvider.category]?.[selectedProvider.provider] && (
<>
<div className="w-16 h-16 mr-3 mb-3 rounded-md shadow-md overflow-hidden flex items-center justify-center bg-white">
<img
src={CONNECTORS_METADATA[selectedProvider.category][selectedProvider.provider].logoPath}
alt={selectedProvider.provider}
className="w-full h-full object-contain"
/>
</div>
<h2 className="text-xl font-semibold mb-6 text-center">
Connect your {selectedProvider.provider.charAt(0).toUpperCase() + selectedProvider.provider.slice(1)} Account
</h2>
</>
)}

<form onSubmit={(e) => { e.preventDefault(); onDomainSubmit(); }} className="w-full space-y-4">
<div className="space-y-1">
<Input
placeholder="Your domain"
className="w-full p-2 border border-gray-300 rounded-md"
onChange={(e) => setEndUserDomain(e.target.value)}
/>
{errors2.end_user_domain && <p className='text-sm font-medium text-red-500'>{errors2.end_user_domain.message}</p>}
</div>

{domainFormats[selectedProvider?.provider?.toLowerCase()] && (
<p className="text-sm text-gray-500 text-center">
e.g., {domainFormats[selectedProvider.provider.toLowerCase()]}
{domainFormats[selectedProvider.provider] && (
<p className="text-sm text-gray-500 mt-1 font-bold">
{domainFormats[selectedProvider.provider]}
</p>
)}

<p className="text-sm text-gray-500 text-center mt-2">
A third-party accountant will be added.
</p>

<Button
type='submit'
className="w-full p-2 rounded-md text-white font-semibold mt-4"
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/src/components/Events/EventsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function EventsTable() {
return (
<>
{transformedEvents && (
<ApiDataTable data={transformedEvents} columns={columns} {...pagination} isLoading={isFetching} />
<ApiDataTable data={transformedEvents} columns={columns as any} {...pagination} isLoading={isFetching} />
)}
</>
);
Expand Down
9 changes: 7 additions & 2 deletions packages/api/scripts/connectorUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,15 @@ function updateSeedSQLFile(seedSQLFile, newServiceDirs, vertical) {
function updateObjectTypes(baseDir, objectType, vertical) {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const servicesDir = path.join(__dirname, baseDir);
const targetFilename = vertical == 'filestorage' ? 'file-storage' : vertical;
const targetFileName =
vertical === 'filestorage'
? 'file-storage'
: vertical === 'marketingautomation'
? 'marketing-automation'
: vertical;
const targetFile = path.join(
__dirname,
`../src/@core/utils/types/original/original.${targetFilename}.ts`,
`../src/@core/utils/types/original/original.${targetFileName}.ts`,
);

const newServiceDirs = scanDirectory(servicesDir);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class MicrosoftDynamicsSalesConnectionService extends AbstractBaseConnect

async handleCallback(opts: OAuthCallbackParams) {
try {
const { linkedUserId, projectId, code, resource } = opts;
const { linkedUserId, projectId, code, organization_name } = opts;
const isNotUnique = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
Expand All @@ -112,7 +112,7 @@ export class MicrosoftDynamicsSalesConnectionService extends AbstractBaseConnect
client_id: CREDENTIALS.CLIENT_ID,
client_secret: CREDENTIALS.CLIENT_SECRET,
code: code,
scope: `https://${resource}/.default offline_access`,
scope: `https://${organization_name}/.default offline_access`,
grant_type: 'authorization_code',
});
const res = await axios.post(
Expand Down Expand Up @@ -141,7 +141,7 @@ export class MicrosoftDynamicsSalesConnectionService extends AbstractBaseConnect
data: {
access_token: this.cryptoService.encrypt(data.access_token),
refresh_token: this.cryptoService.encrypt(data.refresh_token),
account_url: `https://${resource}`,
account_url: `https://${organization_name}`,
expiration_timestamp: new Date(
new Date().getTime() + Number(data.expires_in) * 1000,
),
Expand All @@ -157,7 +157,7 @@ export class MicrosoftDynamicsSalesConnectionService extends AbstractBaseConnect
provider_slug: 'microsoftdynamicssales',
vertical: 'crm',
token_type: 'oauth2',
account_url: `https://${resource}`,
account_url: `https://${organization_name}`,
access_token: this.cryptoService.encrypt(data.access_token),
refresh_token: this.cryptoService.encrypt(data.refresh_token),
expiration_timestamp: new Date(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class SalesforceConnectionService extends AbstractBaseConnectionService {

async handleCallback(opts: OAuthCallbackParams) {
try {
const { linkedUserId, projectId, code, resource: subdomain } = opts;
const { linkedUserId, projectId, code, domain } = opts;
const isNotUnique = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
Expand All @@ -120,7 +120,7 @@ export class SalesforceConnectionService extends AbstractBaseConnectionService {
code: code,
});
const res = await axios.post(
`https://${subdomain}.my.salesforce.com/services/oauth2/token`,
`https://${domain}.my.salesforce.com/services/oauth2/token`,
formData.toString(),
{
headers: {
Expand Down Expand Up @@ -161,7 +161,7 @@ export class SalesforceConnectionService extends AbstractBaseConnectionService {
account_url: (
CONNECTORS_METADATA['crm']['salesforce'].urls
.apiUrl as DynamicApiUrl
)(subdomain),
)(domain),
access_token: this.cryptoService.encrypt(data.access_token),
refresh_token: this.cryptoService.encrypt(data.refresh_token),
expiration_timestamp: new Date(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Injectable } from '@nestjs/common';
import {
AuthStrategy,
CONNECTORS_METADATA,
DynamicApiUrl,
OAuth2AuthData,
providerToType,
} from '@panora/shared';
Expand Down Expand Up @@ -96,7 +97,7 @@ export class SharepointConnectionService extends AbstractBaseConnectionService {

async handleCallback(opts: OAuthCallbackParams) {
try {
const { linkedUserId, projectId, code } = opts;
const { linkedUserId, projectId, code, site, tenant } = opts;
const isNotUnique = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
Expand Down Expand Up @@ -150,8 +151,10 @@ export class SharepointConnectionService extends AbstractBaseConnectionService {
data: {
access_token: this.cryptoService.encrypt(data.access_token),
refresh_token: this.cryptoService.encrypt(data.refresh_token),
account_url: CONNECTORS_METADATA['filestorage']['sharepoint'].urls
.apiUrl as string,
account_url: (
CONNECTORS_METADATA['filestorage']['sharepoint'].urls
.apiUrl as DynamicApiUrl
)(site),
expiration_timestamp: new Date(
new Date().getTime() + Number(data.expires_in) * 1000,
),
Expand All @@ -167,8 +170,10 @@ export class SharepointConnectionService extends AbstractBaseConnectionService {
provider_slug: 'sharepoint',
vertical: 'filestorage',
token_type: 'oauth2',
account_url: CONNECTORS_METADATA['filestorage']['sharepoint'].urls
.apiUrl as string,
account_url: (
CONNECTORS_METADATA['filestorage']['sharepoint'].urls
.apiUrl as DynamicApiUrl
)(site),
access_token: this.cryptoService.encrypt(data.access_token),
refresh_token: this.cryptoService.encrypt(data.refresh_token),
expiration_timestamp: new Date(
Expand Down Expand Up @@ -215,8 +220,7 @@ export class SharepointConnectionService extends AbstractBaseConnectionService {
)) as OAuth2AuthData;

const res = await axios.post(
// `https://app.sharepoint.com/oauth2/tokens`,
`https://login.microsoftonline.com/common/oauth2/v2.0/token`,
`https://app.sharepoint.com/oauth2/tokens`,
formData.toString(),
{
headers: {
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/@core/sync/sync.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ export class CoreSyncService {
try {
await task();
} catch (error) {
console.log(error);
this.logger.error(`File Task failed: ${error.message}`, error);
}
}
Expand Down
Loading

0 comments on commit 091e72f

Please sign in to comment.