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

Integrate gitbeaker API into client codebase #830

Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5c78206
configuration changes
VanessaScherma May 2, 2024
2d2b717
pre-commit-configuration
VanessaScherma May 2, 2024
3d6fb92
Merge branch 'INTO-CPS-Association:feature/distributed-demo' into fea…
VanessaScherma Jun 11, 2024
a3ff550
Merge branch 'INTO-CPS-Association:feature/distributed-demo' into fea…
VanessaScherma Jun 20, 2024
4a9740b
class DigitalTwin added
Jun 20, 2024
e7f2964
configuration changes
Jun 20, 2024
6beacfa
Merge branch 'INTO-CPS-Association:feature/distributed-demo' into fea…
VanessaScherma Jul 30, 2024
663cc3d
Update gitlab.ts and gitlab.test.ts
VanessaScherma Jul 30, 2024
c35450d
Eslint fixes
VanessaScherma Jul 30, 2024
4f8e74a
gitlab class updated
VanessaScherma Aug 1, 2024
f0f1160
Add DigitalTwinSubfolders
VanessaScherma Aug 1, 2024
8536453
Fix
VanessaScherma Aug 1, 2024
a86a47b
Add GitlabDriver and divide GitlabInstance and GitlabDigitalTwin
VanessaScherma Aug 2, 2024
56b412e
Change imports
VanessaScherma Aug 7, 2024
c8dc464
Adds config for managing gitlab code of client
prasadtalasila Aug 16, 2024
8d20c28
Adds instructions for running gitlab code of client
prasadtalasila Aug 16, 2024
f61ea59
Merge branch 'INTO-CPS-Association:feature/distributed-demo' into fea…
VanessaScherma Aug 16, 2024
3f35520
Merge branch 'pr-830' of https://github.com/VanessaScherma/DTaaS into…
VanessaScherma Aug 16, 2024
7806eef
Comments resolved
VanessaScherma Aug 17, 2024
ea3fd03
Refactor
VanessaScherma Aug 18, 2024
41cd332
Refactor
VanessaScherma Aug 18, 2024
d54bbb8
Change GitlabInstance constructor parameters
VanessaScherma Aug 23, 2024
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,4 @@ runner.yml
.workspace

# command scripts for runner
servers/execution/runner/lifecycle*
servers/execution/runner/lifecycle*
2 changes: 1 addition & 1 deletion client/config/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ if (typeof window !== 'undefined') {
REACT_APP_LOGOUT_REDIRECT_URI: 'http://localhost/',
REACT_APP_GITLAB_SCOPES: 'openid profile read_user read_repository api',
};
};
};
3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.8",
"@gitbeaker/rest": "^40.1.2",
"@mui/icons-material": "^5.14.8",
"@mui/material": "^5.14.8",
"@reduxjs/toolkit": "^1.9.7",
Expand Down Expand Up @@ -106,4 +107,4 @@
"last 1 safari version"
]
}
}
}
83 changes: 83 additions & 0 deletions client/src/util/gitlab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Gitlab } from '@gitbeaker/rest';

prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved
interface LogEntry {
status: string;
DTName: string;
runnerTag: string;
error?: Error;
}

interface FolderEntry {
name: string;
path: string;
}

class GitlabInstance {
public username: string;
prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved
public api: InstanceType<typeof Gitlab>;
public logs: LogEntry[];
public subfolders: FolderEntry[] = [];

constructor() {
prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved
this.username = 'user1';
this.api = new Gitlab({
host: 'https://gitlab.com',
token: 'glpat-CJknCUBMj8hSC3oibyxS'
});
this.logs = [];
}

async getProjectId(): Promise<number | null> {
const groupPath = 'DTaaS';
prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved
try {
const group = await this.api.Groups.show(groupPath);
const projects = await this.api.Groups.allProjects(group.id);
const project = projects.find(proj => proj.name === this.username);
if (project) {
return project.id;
}
return null;
} catch (error) {
return null;
}
}

async getTriggerToken(projectId: number): Promise<string | null> {
prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved
try {
const triggers = await this.api.PipelineTriggerTokens.all(projectId);
if (triggers) {
return triggers[0].token;
}
return null;
} catch (error) {
return null;
}
}

async getDTSubfolders(projectId: number): Promise<FolderEntry[]> {
const folderPath = 'digital_twins';
try {
const files = await this.api.Repositories.allRepositoryTrees(projectId, {
path: folderPath,
recursive: false,
});

this.subfolders = files
.filter(file => file.type === 'tree' && file.path !== folderPath)
.map(file => ({
name: file.name,
path: file.path
}));

return this.subfolders;
} catch (error) {
return [];
}
}

executionLogs(): LogEntry[] {
return this.logs;
}
}

export default GitlabInstance;
53 changes: 53 additions & 0 deletions client/src/util/gitlabDigitalTwin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import GitlabInstance from './gitlab';

class DigitalTwin {
public DTName: string;
public gitlabInstance: GitlabInstance;
private lastExecutionStatus: string | null = null;

constructor(DTName: string, gitlabInstance: GitlabInstance) {
this.DTName = DTName;
this.gitlabInstance = gitlabInstance;
}

async execute(runnerTag: string): Promise<boolean> {
prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved
prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved
const projectId = await this.gitlabInstance.getProjectId();
if (projectId === null) {
this.lastExecutionStatus = 'error';
return false;
}

const triggerToken = await this.gitlabInstance.getTriggerToken(projectId);
if (triggerToken === null) {
this.lastExecutionStatus = 'error';
return false;
}

const variables = {
DTName: this.DTName,
RunnerTag: runnerTag,
}

try {
await this.gitlabInstance.api.PipelineTriggerTokens.trigger(
projectId,
'main',
triggerToken,
{ variables }
);
this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag: runnerTag });
this.lastExecutionStatus = 'success';
return true;
} catch (error) {
this.gitlabInstance.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), DTName: this.DTName, runnerTag: runnerTag });
this.lastExecutionStatus = 'error';
return false;
}
}

executionStatus(): string | null {
return this.lastExecutionStatus;
}
}

export default DigitalTwin;
40 changes: 40 additions & 0 deletions client/src/util/gitlabDriver.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use

/* eslint-disable no-use-before-define */

to disable the console errors.

Copy link
Contributor Author

@VanessaScherma VanessaScherma Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. In this case, I just used /* eslint-disable no-console */ as suggested below to disable the console errors.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import GitlabInstance from './gitlab';
import DigitalTwin from './gitlabDigitalTwin';

class GitlabDriver {
public static async run(): Promise<void> {
const gitlabInstance = new GitlabInstance();
console.log('GitLab username:', gitlabInstance.username);

Check failure on line 7 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement
console.log('GitLab logs:', gitlabInstance.logs);

Check failure on line 8 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement
console.log('GitLab subfolders:', gitlabInstance.subfolders);

Check failure on line 9 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement

const projectId = await gitlabInstance.getProjectId() || 0;
console.log('Project id:', projectId);

Check failure on line 12 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement

const subfolders = await gitlabInstance.getDTSubfolders(projectId);
console.log('Subfolders:', subfolders);

Check failure on line 15 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement

const dtName = subfolders[0].name;
const runnerTag = 'dtaas';

const triggerToken = await gitlabInstance.getTriggerToken(projectId);
console.log('Trigger token:', triggerToken);

Check failure on line 21 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement

const digitalTwin = new DigitalTwin(dtName, gitlabInstance);
const result = await digitalTwin.execute(runnerTag);

console.log('Execution Result:', result);

Check failure on line 26 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement

const lastExecutionStatus = digitalTwin.executionStatus();
console.log('Execution Status:', lastExecutionStatus);

Check failure on line 29 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement

const logs = gitlabInstance.executionLogs();
console.log('Execution Logs:', logs);

Check failure on line 32 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement
}
}

GitlabDriver.run().catch(error => {
console.error('Error executing GitlabDriver:', error);

Check failure on line 37 in client/src/util/gitlabDriver.ts

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected console statement
});

export default GitlabDriver;
135 changes: 135 additions & 0 deletions client/test/unitTests/Util/gitlab.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Gitlab } from '@gitbeaker/rest';
import { ProjectSchema, PipelineTriggerTokenSchema, GroupSchema, RepositoryTreeSchema } from '@gitbeaker/rest';
import GitlabInstance from '../../../src/util/gitlab';
prasadtalasila marked this conversation as resolved.
Show resolved Hide resolved

const mockApi = {
Groups: {
show: jest.fn(),
allProjects: jest.fn()
},
PipelineTriggerTokens: {
all: jest.fn(),
trigger: jest.fn()
},
Repositories: {
allRepositoryTrees: jest.fn()
}
};

describe('GitlabInstance', () => {
let gitlab: GitlabInstance;

beforeEach(() => {
gitlab = new GitlabInstance();
gitlab.api = mockApi as unknown as InstanceType<typeof Gitlab>;
});

it('should fetch project ID successfully', async () => {
mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' } as GroupSchema);
mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]);

const projectId = await gitlab.getProjectId();

expect(projectId).toBe(1);
expect(mockApi.Groups.show).toHaveBeenCalledWith('DTaaS');
expect(mockApi.Groups.allProjects).toHaveBeenCalledWith(1);
});

it('should handle project ID not found', async () => {
mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' } as GroupSchema);
mockApi.Groups.allProjects.mockResolvedValue([]);

const projectId = await gitlab.getProjectId();

expect(projectId).toBeNull();
});

it('should handle errors fetching project ID', async () => {
mockApi.Groups.show.mockRejectedValue(new Error('API error'));

const projectId = await gitlab.getProjectId();

expect(projectId).toBeNull();
});

it('should fetch trigger token successfully', async () => {
mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]);

const token = await gitlab.getTriggerToken(1);

expect(token).toBe('test-token');
expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1);
});

it('should handle no trigger tokens found', async () => {
mockApi.PipelineTriggerTokens.all.mockResolvedValue([]);

const token = await gitlab.getTriggerToken(1);

expect(token).toBeNull();
expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1);
});

it('should handle undefined trigger tokens', async () => {
mockApi.PipelineTriggerTokens.all.mockResolvedValue(undefined);

const token = await gitlab.getTriggerToken(1);

expect(token).toBeNull();
});

it('should handle errors fetching trigger token', async () => {
mockApi.PipelineTriggerTokens.all.mockRejectedValue(new Error('API error'));

const token = await gitlab.getTriggerToken(1);

expect(token).toBeNull();
expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1);
});

it('should fetch DT subfolders successfully', async () => {
mockApi.Repositories.allRepositoryTrees.mockResolvedValue([
{ name: 'subfolder1', path: 'digital_twins/subfolder1', type: 'tree' } as RepositoryTreeSchema,
{ name: 'subfolder2', path: 'digital_twins/subfolder2', type: 'tree' } as RepositoryTreeSchema,
{ name: 'file1', path: 'digital_twins/file1', type: 'blob' } as RepositoryTreeSchema
]);

const subfolders = await gitlab.getDTSubfolders(1);

expect(subfolders).toHaveLength(2);
expect(subfolders).toEqual([
{ name: 'subfolder1', path: 'digital_twins/subfolder1' },
{ name: 'subfolder2', path: 'digital_twins/subfolder2' }
]);
expect(mockApi.Repositories.allRepositoryTrees).toHaveBeenCalledWith(1, {
path: 'digital_twins',
recursive: false
});
});

it('should handle errors fetching DT subfolders', async () => {
mockApi.Repositories.allRepositoryTrees.mockRejectedValue(new Error('API error'));

const subfolders = await gitlab.getDTSubfolders(1);

expect(subfolders).toEqual([]);
});

it('should return execution logs', () => {
const mockLog = {
status: 'success',
DTName: 'test-DTName',
runnerTag: 'test-runnerTag',
error: undefined
};

gitlab.logs.push(mockLog);

const logs = gitlab.executionLogs();

expect(logs).toHaveLength(1);
expect(logs[0].status).toBe('success');
expect(logs[0].DTName).toBe('test-DTName');
expect(logs[0].runnerTag).toBe('test-runnerTag');
});
});
Loading