Skip to content

Commit

Permalink
Adds gitlab API into client codebase (#894)
Browse files Browse the repository at this point in the history
- Uses gitbeaker API to showcase gitlab API integration into react client codebase
- This PR builds on top of PR #830
  • Loading branch information
prasadtalasila authored Aug 29, 2024
1 parent a335ae4 commit 228ed5c
Show file tree
Hide file tree
Showing 14 changed files with 703 additions and 9 deletions.
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*
52 changes: 52 additions & 0 deletions client/DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,55 @@ port and basename options in the docker-based development environment.
Each new release of client web application is published as a docker
container image. Please see [publishing](../docker/README.md) page
for more information publishing docker images.

## Gitlab Integration

The client codebase has been using Gitlab for OAuth2 only. There is
an ongoing effort to integrate Gitlab CI/CD capabilities to automate
the execution of Digital Twins. This code is in alpha stage and is
available in `src/util/gitlab*.ts`.

The gitlab code requires
[gitlab personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)
for a gitlab account. Remember to select all scopes for access token.
Specifically, select the following scopes.

```txt
api, read_api, read_user, create_runner, k8s_proxy, read_repository, write_repository, ai_features
```

The token information needs to be updated in `config/gitlab.json`.

Once the token configuration is in place, the gitlab code can be developed
and tested using the following yarn commands.

```bash
yarn gitlab:compile
yarn gitlab:run
```

These two commands run `src/util/gitlabDriver.ts` code to check the correct
functioning of the gitlab code placed in `src/util` directory.

A piece of code in `src/util/gitlabDriver.ts` checks for correct execution
of a DT in gitlab runner. The token owner must have a hosted runner
in order for this piece of code to be executed successfully.
Otherwise, the following error appears.

```log
....
Execution Result: false
Execution Status: error
Execution Logs: [
{
status: 'error',
error: Error: GitbeakerRequestError: Not Found
at DigitalTwin.<anonymous> (file:///C:/Users/au598657/git/DTaaS/client/dist/gitlabDigitalTwin.js:32:73)
at Generator.throw (<anonymous>)
at rejected (file:///C:/Users/au598657/git/DTaaS/client/dist/gitlabDigitalTwin.js:5:65)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5),
DTName: 'hello-world',
runnerTag: 'dtaas'
}
]
```
5 changes: 5 additions & 0 deletions client/config/gitlab.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"username": "username",
"host": "https://foo.com/gitlab",
"oauth_token": "xxxxxxxxxxxxxxxxxxxx"
}
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/jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"build",
"src/index.tsx",
"src/AppProvider.tsx",
"src/store/store.ts"
"src/store/store.ts",
"src/util/gitlabDriver.ts"
],
"modulePathIgnorePatterns": [
"test/e2e",
Expand Down
13 changes: 8 additions & 5 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@into-cps-association/dtaas-web",
"version": "0.3.6",
"version": "0.4.0",
"description": "Web client for Digital Twin as a Service (DTaaS)",
"main": "index.tsx",
"author": "prasadtalasila <[email protected]> (http://prasad.talasila.in/)",
Expand All @@ -15,14 +15,16 @@
"private": false,
"type": "module",
"scripts": {
"build": "npx react-scripts build",
"clean": "npx rimraf build/ node_modules/ coverage/ playwright-report/ *.svg",
"build": "npx shx cp config/gitlab.json src/util/gitlab.json && npx react-scripts build && npx rimraf src/util/gitlab.json",
"clean": "npx rimraf build/ dist/ node_modules/ coverage/ playwright-report/ *.svg src/util/gitlab.json",
"config:dev": "npx shx cp config/dev.js public/env.js && npx shx cp config/dev.js build/env.js",
"config:local": "npx shx cp config/local.js public/env.js && npx shx cp config/local.js build/env.js",
"config:prod": "npx shx cp config/prod.js public/env.js && npx shx cp config/prod.js build/env.js",
"config:test": "npx shx cp config/test.js public/env.js && npx shx cp config/test.js build/env.js",
"develop": "npx react-scripts start",
"develop": "npx shx cp config/gitlab.json src/util/gitlab.json && npx react-scripts start",
"format": "prettier --ignore-path ../.gitignore --write \"**/*.{ts,tsx,css,scss}\"",
"gitlab:compile": "npx shx cp config/gitlab.json src/util/gitlab.json && npx tsc --project tsconfig.gitlab.json && npx rimraf src/util/gitlab.json",
"gitlab:run": "node dist/gitlabDriver.js",
"graph": "npx madge --image src.svg src && npx madge --image test.svg test",
"start": "serve -s build -l 4000",
"stop": "npx kill-port 4000",
Expand All @@ -44,6 +46,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 +109,4 @@
"last 1 safari version"
]
}
}
}
85 changes: 85 additions & 0 deletions client/src/util/gitlab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Gitlab } from '@gitbeaker/rest';

const GROUP_NAME = 'DTaaS';
const DT_DIRECTORY = 'digital_twins';

interface LogEntry {
status: string;
DTName: string;
runnerTag: string;
error?: Error;
}

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

class GitlabInstance {
public username: string | null;

public api: InstanceType<typeof Gitlab>;

public logs: LogEntry[];

public subfolders: FolderEntry[];

constructor(username: string, host: string, oauthToken: string) {
this.username = username;
this.api = new Gitlab({
host,
oauthToken,
});
this.logs = [];
this.subfolders = [];
}

async getProjectId(): Promise<number | null> {
let projectId: number | null = null;

const group = await this.api.Groups.show(GROUP_NAME);
const projects = await this.api.Groups.allProjects(group.id);
const project = projects.find((proj) => proj.name === this.username);

if (project) {
projectId = project.id;
}

return projectId;
}

async getTriggerToken(projectId: number): Promise<string | null> {
let token: string | null = null;

const triggers = await this.api.PipelineTriggerTokens.all(projectId);
if (triggers && triggers.length > 0) {
token = triggers[0].token;
}
return token;
}

async getDTSubfolders(projectId: number): Promise<FolderEntry[]> {
let subfolders: FolderEntry[] = [];

const files = await this.api.Repositories.allRepositoryTrees(projectId, {
path: DT_DIRECTORY,
recursive: false,
});

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

this.subfolders = subfolders;
return subfolders;
}

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

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

class DigitalTwin {
public DTName: string;

public gitlabInstance: GitlabInstance;

public lastExecutionStatus: string | null = null;

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

async execute(runnerTag: string): Promise<boolean> {
const projectId = await this.gitlabInstance.getProjectId();
const triggerToken =
projectId && (await this.gitlabInstance.getTriggerToken(projectId));

if (!projectId || !triggerToken) {
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,
});
this.lastExecutionStatus = 'success';
return true;
} catch (error) {
this.gitlabInstance.logs.push({
status: 'error',
error: new Error(String(error)),
DTName: this.DTName,
runnerTag,
});
this.lastExecutionStatus = 'error';
return false;
}
}

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

export default DigitalTwin;
46 changes: 46 additions & 0 deletions client/src/util/gitlabDriver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* eslint-disable no-console */
import GitlabInstance from './gitlab.js';
import DigitalTwin from './gitlabDigitalTwin.js';
import config from './gitlab.json' assert { type: 'json' };

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

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

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

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

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

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

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

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

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

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

export default GitlabDriver;
Loading

0 comments on commit 228ed5c

Please sign in to comment.