Skip to content

Commit

Permalink
feat: Update uploadProject method to support IR. Optimize walk method (
Browse files Browse the repository at this point in the history
  • Loading branch information
joe-yeager authored Dec 18, 2024
1 parent 7957ded commit fbfadb9
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 8 deletions.
47 changes: 47 additions & 0 deletions api/__tests__/projects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const createReadStreamMock = createReadStream as jest.MockedFunction<
typeof createReadStream
>;

const httpPostMock = http.post as jest.MockedFunction<typeof http.post>;

describe('api/projects', () => {
const accountId = 999999;
const projectId = 888888;
Expand Down Expand Up @@ -132,6 +134,51 @@ describe('api/projects', () => {
headers: { 'Content-Type': 'multipart/form-data' },
});
});

it('should call the v3 api when optional intermediateRepresentation is provided', async () => {
// @ts-expect-error Wants full axios response
httpPostMock.mockResolvedValue({
data: {
createdBuildId: 123,
},
});

const intermediateRepresentation = {
intermediateNodesIndexedByUid: {
'calling-1': {
componentType: 'APP',
uid: 'calling-1',
config: {},
componentDeps: {},
files: {},
},
},
};

await uploadProject(
accountId,
projectName,
projectFile,
uploadMessage,
platformVersion,
intermediateRepresentation
);
expect(http.post).toHaveBeenCalledTimes(1);
expect(http.post).toHaveBeenCalledWith(accountId, {
url: `project-components-external/v3/upload/new-api`,
timeout: 60_000,
data: {
projectFilesZip: formData,
platformVersion,
uploadRequest: JSON.stringify({
...intermediateRepresentation,
projectName,
buildMessage: uploadMessage,
}),
},
headers: { 'Content-Type': 'multipart/form-data' },
});
});
});

describe('fetchProject', () => {
Expand Down
35 changes: 31 additions & 4 deletions api/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ProjectSettings,
FetchPlatformVersionResponse,
WarnLogsResponse,
UploadIRResponse,
} from '../types/Project';
import { Build, FetchProjectBuildsResponse } from '../types/Build';
import {
Expand All @@ -28,6 +29,8 @@ const PROJECTS_LOGS_API_PATH = 'dfs/logging/v1';
const DEVELOPER_PROJECTS_API_PATH = 'developer/projects/v1';
const MIGRATIONS_API_PATH = 'dfs/migrations/v1';

const PROJECTS_V3_API_PATH = 'project-components-external/v3';

export function fetchProjects(
accountId: number
): HubSpotPromise<FetchProjectResponse> {
Expand All @@ -48,21 +51,45 @@ export function createProject(
});
}

export function uploadProject(
export async function uploadProject(
accountId: number,
projectName: string,
projectFile: string,
uploadMessage: string,
platformVersion?: string
): HubSpotPromise<UploadProjectResponse> {
platformVersion?: string,
intermediateRepresentation?: unknown
): HubSpotPromise<UploadProjectResponse | UploadIRResponse> {
if (intermediateRepresentation) {
const formData = {
projectFilesZip: fs.createReadStream(projectFile),
platformVersion,
uploadRequest: JSON.stringify({
...intermediateRepresentation,
projectName,
buildMessage: uploadMessage,
}),
};

const response = await http.post<UploadIRResponse>(accountId, {
url: `${PROJECTS_V3_API_PATH}/upload/new-api`,
timeout: 60_000,
data: formData,
headers: { 'Content-Type': 'multipart/form-data' },
});

// Remap the response to match the expected shape
response.data.buildId = response.data.createdBuildId;

return response;
}

const formData: FormData = {
file: fs.createReadStream(projectFile),
uploadMessage,
};
if (platformVersion) {
formData.platformVersion = platformVersion;
}

return http.post<UploadProjectResponse>(accountId, {
url: `${PROJECTS_API_PATH}/upload/${encodeURIComponent(projectName)}`,
timeout: 60_000,
Expand Down
16 changes: 12 additions & 4 deletions lib/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ export function flattenAndRemoveSymlinks(

const generateRecursiveFilePromise = async (
dir: string,
file: string
file: string,
ignoreDirs?: string[]
): Promise<FileData> => {
return getFileInfoAsync(dir, file).then(fileData => {
return new Promise(resolve => {
if (fileData.type === STAT_TYPES.DIRECTORY) {
walk(fileData.filepath).then(files => {
walk(fileData.filepath, ignoreDirs).then(files => {
resolve({ ...fileData, files });
});
} else {
Expand All @@ -57,10 +58,17 @@ const generateRecursiveFilePromise = async (
});
};

export async function walk(dir: string): Promise<Array<string>> {
export async function walk(
dir: string,
ignoreDirs?: string[]
): Promise<Array<string>> {
function processFiles(files: Array<string>) {
// If the directory is in the ignore list, return an empty array to skip the directory contents
if (ignoreDirs?.some(ignored => dir.includes(ignored))) {
return [];
}
return Promise.all(
files.map(file => generateRecursiveFilePromise(dir, file))
files.map(file => generateRecursiveFilePromise(dir, file, ignoreDirs))
);
}

Expand Down
5 changes: 5 additions & 0 deletions types/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export type UploadProjectResponse = {
};
};

export type UploadIRResponse = {
buildId?: number;
createdBuildId: number;
};

export type ProjectSettings = {
isAutoDeployEnabled: boolean;
};
Expand Down

0 comments on commit fbfadb9

Please sign in to comment.