Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Sharepoint connector #742

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,16 @@ BOX_FILESTORAGE_CLOUD_CLIENT_SECRET=
# Onedrive
ONEDRIVE_FILESTORAGE_CLOUD_CLIENT_ID=
ONEDRIVE_FILESTORAGE_CLOUD_CLIENT_SECRET=
# dropbox
# Dropbox
DROPBOX_FILESTORAGE_CLOUD_CLIENT_ID=
DROPBOX_FILESTORAGE_CLOUD_CLIENT_SECRET=

# Google Drive
GOOGLEDRIVE_FILESTORAGE_CLOUD_CLIENT_ID=
GOOGLEDRIVE_FILESTORAGE_CLOUD_CLIENT_SECRET=

# Google Drive
# Sharepoint
SHAREPOINT_FILESTORAGE_CLOUD_CLIENT_ID=
SHAREPOINT_FILESTORAGE_CLOUD_CLIENT_SECRET=

# Google Drive
DROPBOX_FILESTORAGE_CLOUD_CLIENT_ID=
DROPBOX_FILESTORAGE_CLOUD_CLIENT_SECRET=


# ================================================
# ECOMMERCE
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ services:
NEXT_PUBLIC_POSTHOG_KEY: ${POSTHOG_KEY}
NEXT_PUBLIC_POSTHOG_HOST: ${POSTHOG_HOST}
NEXT_PUBLIC_DISTRIBUTION: ${DISTRIBUTION}
NEXT_PUBLIC_BACKEND_DOMAIN: https://localhost:3000
NEXT_PUBLIC_BACKEND_DOMAIN: http://localhost:3000
NEXT_PUBLIC_MAGIC_LINK_DOMAIN: ${NEXT_PUBLIC_MAGIC_LINK_DOMAIN}
NEXT_PUBLIC_WEBAPP_DOMAIN: ${NEXT_PUBLIC_WEBAPP_DOMAIN}
NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS: ${REDIRECT_TUNNEL_INGRESS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export class SharepointConnectionService extends AbstractBaseConnectionService {

// get site_id from tenant and sitename
const site_details = await axios.get(
`https://graph.microsoft.com/v1.0/sites/${site}.sharepoint.com:/sites/${tenant}`,
`https://graph.microsoft.com/v1.0/sites/${tenant}.sharepoint.com:/sites/${site}`,
{
headers: {
Authorization: `Bearer ${data.access_token}`,
Expand Down
93 changes: 84 additions & 9 deletions packages/api/src/filestorage/drive/services/sharepoint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import { SyncParam } from '@@core/utils/types/interface';
import { FileStorageObject } from '@filestorage/@lib/@types';
import { IDriveService } from '@filestorage/drive/types';
import { Injectable } from '@nestjs/common';
import axios from 'axios';
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { ServiceRegistry } from '../registry.service';
import { SharepointDriveOutput } from './types';
import { DesunifyReturnType } from '@@core/utils/types/desunify.input';
import { OriginalDriveOutput } from '@@core/utils/types/original/original.file-storage';

@Injectable()
export class SharepointService implements IDriveService {
private readonly MAX_RETRIES: number = 6;
private readonly INITIAL_BACKOFF_MS: number = 1000;

constructor(
private prisma: PrismaService,
private logger: LoggerService,
Expand All @@ -30,8 +33,11 @@ export class SharepointService implements IDriveService {
driveData: DesunifyReturnType,
linkedUserId: string,
): Promise<ApiResponse<OriginalDriveOutput>> {
// No API to add drive in Sharepoint
return;
return {
data: null,
message: 'Add drive not supported for SharePoint.',
statusCode: 501,
};
}

async sync(data: SyncParam): Promise<ApiResponse<SharepointDriveOutput[]>> {
Expand All @@ -46,26 +52,95 @@ export class SharepointService implements IDriveService {
},
});

const resp = await axios.get(`${connection.account_url}/drives`, {
const config: AxiosRequestConfig = {
method: 'get',
url: `${connection.account_url}/drives`,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.cryptoService.decrypt(
connection.access_token,
)}`,
},
});
};

const resp: AxiosResponse = await this.makeRequestWithRetry(config);

const drives: SharepointDriveOutput[] = resp.data.value;
this.logger.log(`Synced sharepoint drives !`);
this.logger.log(`Synced SharePoint drives successfully.`);

return {
data: drives,
message: 'Sharepoint drives retrived',
message: 'SharePoint drives retrieved successfully.',
statusCode: 200,
};
} catch (error) {
console.log(error.response);
} catch (error: any) {
this.logger.error(
`Error syncing SharePoint drives: ${error.message}`,
error,
);
throw error;
}
}

private async makeRequestWithRetry(
config: AxiosRequestConfig,
): Promise<AxiosResponse> {
let attempts = 0;
let backoff: number = this.INITIAL_BACKOFF_MS;

while (attempts < this.MAX_RETRIES) {
try {
const response: AxiosResponse = await axios(config);
return response;
} catch (error: any) {
attempts++;

if (
(error.response && error.response.status === 429) ||
(error.response && error.response.status >= 500) ||
error.code === 'ECONNABORTED' ||
error.code === 'ETIMEDOUT' ||
error.response?.code === 'ETIMEDOUT'
) {
const retryAfter = this.getRetryAfter(
error.response?.headers['retry-after'],
);
const delayTime: number = Math.max(retryAfter * 1000, backoff);

this.logger.warn(
`Request failed with ${
error.code || error.response?.status
}. Retrying in ${delayTime}ms (Attempt ${attempts}/${
this.MAX_RETRIES
})`,
);

await this.delay(delayTime);
backoff *= 2;
continue;
}

this.logger.error(`Request failed: ${error.message}`, error);
throw error;
}
}

this.logger.error(
'Max retry attempts reached. Request failed.',
SharepointService.name,
);
throw new Error('Max retry attempts reached.');
}

private getRetryAfter(retryAfterHeader: string | undefined): number {
if (!retryAfterHeader) {
return 1;
}
const retryAfterSeconds: number = parseInt(retryAfterHeader, 10);
return isNaN(retryAfterSeconds) ? 1 : retryAfterSeconds;
}

private delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export interface IdentitySet {
readonly phone?: Identity;
/** Identity representing a user. */
readonly user?: Identity;
/** Identity representing a group. */
readonly group?: Identity;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/api/src/filestorage/file/file.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SyncService } from './sync/sync.service';
import { GoogleDriveQueueProcessor } from './services/googledrive/processor';
import { FolderModule } from '../folder/folder.module';
import { OnedriveQueueProcessor } from './services/onedrive/processor';
import { SharepointQueueProcessor } from './services/sharepoint/processor';

@Module({
imports: [forwardRef(() => FolderModule)],
Expand All @@ -45,6 +46,7 @@ import { OnedriveQueueProcessor } from './services/onedrive/processor';
GoogleDriveService,
GoogleDriveQueueProcessor,
OnedriveQueueProcessor,
SharepointQueueProcessor,
],
exports: [SyncService, ServiceRegistry, GoogleDriveService],
})
Expand Down
Loading
Loading