diff --git a/packages/server/modules/acc/clients/autodesk.ts b/packages/server/modules/acc/clients/autodesk.ts new file mode 100644 index 000000000..b2eaee051 --- /dev/null +++ b/packages/server/modules/acc/clients/autodesk.ts @@ -0,0 +1,60 @@ +import type { ModelDerivativeServiceDesignManifest } from '@/modules/acc/domain/types' + +const invokeRequest = async (params: { + url: string + token: string + method?: RequestInit['method'] + body?: URLSearchParams +}) => { + const { url, method = 'get', body, token } = params + + const response = await fetch(url, { + method, + headers: { + 'Content-Type': + body && body instanceof URLSearchParams + ? 'application/x-www-form-urlencoded' + : 'application/json', + Authorization: `Bearer ${token}` + }, + body + }) + + return (await response.json()) as T +} + +export const getManifestByUrn = async ( + urn: string +): Promise => { + const clientId = '5Y2LzxsL3usaD1xAMyElBY8mcN6XKyfHfulZDV3up0jfhN5Y' + const clientSecret = + 'qHyGqaP4zCWLyS2lp04qBDOC1giIupPzJPmLFKGFHKZrPYYpan27zF8vlhQr1RYL' + + const token = Buffer.from(`${clientId}:${clientSecret}`, 'utf8').toString('base64') + const tokens = await fetch( + 'https://developer.api.autodesk.com/authentication/v2/token', + { + method: 'POST', + body: new URLSearchParams({ + /* eslint-disable-next-line */ + grant_type: 'client_credentials', + scope: 'data:read account:read viewables:read' + }), + headers: { + Authorization: `Basic ${token}`, + Accept: 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded' + } + } + ) + + const data = await tokens.json() + + const { access_token: accessToken } = data + + return await invokeRequest({ + url: `https://developer.api.autodesk.com/modelderivative/v2/regions/eu/designdata/${urn}/manifest`, + token: accessToken, + method: 'GET' + }) +} diff --git a/packages/server/modules/acc/domain/logic.ts b/packages/server/modules/acc/domain/logic.ts new file mode 100644 index 000000000..50808a0a0 --- /dev/null +++ b/packages/server/modules/acc/domain/logic.ts @@ -0,0 +1,12 @@ +import type { ModelDerivativeServiceDesignManifest } from '@/modules/acc/domain/types' + +export const isReadyForImport = ( + manifest: ModelDerivativeServiceDesignManifest +): boolean => { + return ( + manifest.derivatives?.some( + ({ status, outputType, overrideOutputType }) => + [outputType, overrideOutputType].includes('svf2') && status === 'success' + ) ?? false + ) +} diff --git a/packages/server/modules/acc/domain/types.ts b/packages/server/modules/acc/domain/types.ts index 9523337ee..7ccdf4981 100644 --- a/packages/server/modules/acc/domain/types.ts +++ b/packages/server/modules/acc/domain/types.ts @@ -27,3 +27,30 @@ export type AccSyncItemStatus = | 'FAILED' | 'PAUSED' | 'SUCCEEDED' + +export type ModelDerivativeServiceDesignManifest = { + type: 'manifest' + region: string + /* special base64 encoded */ + urn: string + derivatives?: ModelDerivativeServiceDesignManifestDerivative[] +} + +export type ModelDerivativeServiceDesignManifestDerivative = { + name?: string + status: 'pending' | 'inprogress' | 'success' | 'failed' | 'timeout' + progress: 'complete' | `${number}%` + outputType: + | 'dwg' + | 'fbx' + | 'ifc' + | 'iges' + | 'obj' + | 'step' + | 'stl' + | 'svf' + | 'svf2' + | 'thumbnail' + /** Sometimes `outputType` is "svf" and `overrideOutputType` is "svf2" */ + overrideOutputType?: string +} diff --git a/packages/server/modules/acc/index.ts b/packages/server/modules/acc/index.ts index 786a74839..cf63e90f1 100644 --- a/packages/server/modules/acc/index.ts +++ b/packages/server/modules/acc/index.ts @@ -16,7 +16,10 @@ import { import { Scopes, TIME_MS } from '@speckle/shared' import type { ScheduleExecution } from '@/modules/core/domain/scheduledTasks/operations' import { AccSyncItems } from '@/modules/acc/dbSchema' -import type { AccSyncItem } from '@/modules/acc/domain/types' +import type { + AccSyncItem, + ModelDerivativeServiceDesignManifest +} from '@/modules/acc/domain/types' import type { InsertableAutomationRun } from '@/modules/automate/repositories/automations' import { getAutomationFactory, @@ -38,6 +41,8 @@ import { import { TokenResourceIdentifierType } from '@/modules/core/graph/generated/graphql' import { getServerOrigin } from '@/modules/shared/helpers/envHelper' import type { VersionCreatedTriggerManifest } from '@/modules/automate/helpers/types' +import { getManifestByUrn } from '@/modules/acc/clients/autodesk' +import { isReadyForImport } from '@/modules/acc/domain/logic' export default function accRestApi(app: Express) { const sessionMiddleware = sessionMiddlewareFactory() @@ -137,6 +142,57 @@ export default function accRestApi(app: Express) { } }) + app.get('/acc/download', sessionMiddleware, async (req, res) => { + const clientId = '5Y2LzxsL3usaD1xAMyElBY8mcN6XKyfHfulZDV3up0jfhN5Y' + const clientSecret = + 'qHyGqaP4zCWLyS2lp04qBDOC1giIupPzJPmLFKGFHKZrPYYpan27zF8vlhQr1RYL' + + console.log('AAAAAA') + const token = Buffer.from(`${clientId}:${clientSecret}`, 'utf8').toString('base64') + const tokens = await fetch( + 'https://developer.api.autodesk.com/authentication/v2/token', + { + method: 'POST', + body: new URLSearchParams({ + grant_type: 'client_credentials', + scope: 'data:read account:read viewables:read' + }), + headers: { + Authorization: `Basic ${token}`, + Accept: 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded' + } + } + ) + + const data = await tokens.json() + + console.log(data) + + const { access_token } = data + + // https://developer.api.autodesk.com/modelderivative/v2/designdata/{urn}/manifest + // (EMEA) https://developer.api.autodesk.com/modelderivative/v2/regions/eu/designdata/{urn}/manifest + + const urn = + 'dXJuOmFkc2sud2lwZW1lYTpmcy5maWxlOnZmLjVORWw3ajZJVF9PSmRDdjVDWFNHTlE_dmVyc2lvbj0xNA' + const response = await fetch( + `https://developer.api.autodesk.com/modelderivative/v2/regions/eu/designdata/${urn}/manifest`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${access_token}` + } + } + ) + + const manifest: ModelDerivativeServiceDesignManifest = await response.json() + + console.log(manifest) + + res.status(200).send('OK') + }) + app.post('/acc/sync-item-created', sessionMiddleware, async (req, res) => { const { accHubUrn } = req.body @@ -217,6 +273,15 @@ const schedulePendingAccSyncItemsPoll = () => { const projectDb = await getProjectDbClient({ projectId: syncItem.projectId }) + const manifest = await getManifestByUrn(syncItem.accFileVersionUrn) + + console.log(manifest) + + if (!isReadyForImport(manifest)) { + console.log('NOT READY') + continue + } + await projectDb.table(AccSyncItems.name).update({ status: 'SYNCING' })