src/frontend/packages/git/src/shared/components/git-registration/git-registration.component.ts
import { Component, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import {
CreateEndpointHelperComponent,
} from 'frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-helper';
import { Observable, Subscription } from 'rxjs';
import { filter, first, map, pairwise } from 'rxjs/operators';
import { EndpointsService } from '../../../../../core/src/core/endpoints.service';
import { getIdFromRoute } from '../../../../../core/src/core/utils.service';
import { ConnectEndpointConfig } from '../../../../../core/src/features/endpoints/connect.service';
import { StepOnNextFunction } from '../../../../../core/src/shared/components/stepper/step/step.component';
import { SessionService } from '../../../../../core/src/shared/services/session.service';
import { CurrentUserPermissionsService } from '../../../../../core/src/core/permissions/current-user-permissions.service';
import { UserProfileService } from '../../../../../core/src/core/user-profile.service';
import { SnackBarService } from '../../../../../core/src/shared/services/snackbar.service';
import { getFullEndpointApiUrl } from '../../../../../store/src/endpoint-utils';
import { entityCatalog } from '../../../../../store/src/public-api';
import { ActionState } from '../../../../../store/src/reducers/api-request-reducer/types';
import { stratosEntityCatalog } from '../../../../../store/src/stratos-entity-catalog';
import { GIT_ENDPOINT_SUB_TYPES, GIT_ENDPOINT_TYPE } from '../../../store/git-entity-factory';
import { GitSCMService } from '../../scm/scm.service';
interface EndpointSubTypes {
[subType: string]: GithubTypes;
}
interface GithubTypes {
label: string;
description: string;
types: {
[key: string]: GithubType;
};
}
interface GithubType {
url: string;
label: string;
description: string[];
name?: string;
exists?: boolean;
urlSuffix?: string;
}
enum GitTypeKeys {
GITHUB_COM = 'githubdotcom',
GITHUB_ENTERPRISE = 'githubenterprize',
GITLAB_COM = 'githubdotcom',
GITLAB_ENTERPRISE = 'githubenterprize',
}
@Component({
selector: 'app-git-registration',
templateUrl: './git-registration.component.html',
styleUrls: ['./git-registration.component.scss'],
})
export class GitRegistrationComponent extends CreateEndpointHelperComponent implements OnDestroy {
public gitTypes: EndpointSubTypes;
public epSubType: GIT_ENDPOINT_SUB_TYPES;
registerForm: FormGroup;
private sub: Subscription;
public showEndpointFields = false;
validate: Observable<boolean>;
urlValidation: string;
constructor(
gitSCMService: GitSCMService,
activatedRoute: ActivatedRoute,
private fb: FormBuilder,
private snackBarService: SnackBarService,
private endpointsService: EndpointsService,
public sessionService: SessionService,
public currentUserPermissionsService: CurrentUserPermissionsService,
public userProfileService: UserProfileService
) {
super(sessionService, currentUserPermissionsService, userProfileService);
this.epSubType = getIdFromRoute(activatedRoute, 'subtype');
const githubLabel = entityCatalog.getEndpoint(GIT_ENDPOINT_TYPE, GIT_ENDPOINT_SUB_TYPES.GITHUB).definition.label || 'Github';
const gitlabLabel = entityCatalog.getEndpoint(GIT_ENDPOINT_TYPE, GIT_ENDPOINT_SUB_TYPES.GITLAB).definition.label || 'Gitlab';
const publicGithubUrl = gitSCMService.getSCM('github', null).getPublicApi();
const publicGitlabUrl = gitSCMService.getSCM('gitlab', null).getPublicApi();
// Set a default/starting option
this.gitTypes = {
[GIT_ENDPOINT_SUB_TYPES.GITHUB]: {
label: githubLabel,
description: '',
types: {
[GitTypeKeys.GITHUB_COM]: {
label: 'github.com',
url: publicGithubUrl,
name: 'GitHub',
description: [
`Registering github.com allows you to connect with a Personal Access Token and access your public and private ${githubLabel} repositories.`,
'Note: Stratos allows you to access github.com without registering this endpoint, but you are limited to accessing public repositories.'
],
},
[GitTypeKeys.GITHUB_ENTERPRISE]: {
label: 'Github Enterprise',
url: null,
description: [
`Register your own GitHub Enterprise server.`,
'Registering an endpoint allows you to access public repositories. Connect with a Personal Access Token to additionally access your private repositories',
],
}
}
},
[GIT_ENDPOINT_SUB_TYPES.GITLAB]: {
label: gitlabLabel,
description: '',
types: {
[GitTypeKeys.GITLAB_COM]: {
label: 'gitlab.com',
url: publicGitlabUrl,
name: 'GitLab',
description: [
`Registering gitlab.com allows you to connect with a Personal Access Token and access your public and private ${gitlabLabel} repositories.`,
'Note: Stratos allows you to access gitlab.com without registering this endpoint, but you are limited to accessing public repositories.'
],
},
[GitTypeKeys.GITLAB_ENTERPRISE]: {
label: 'Gitlab Enterprise',
url: null,
description: [
`Register your own Gitlab Enterprise server.`,
'Registering an endpoint allows you to access public repositories. Connect with a Personal Access Token to additionally access your private repositories',
],
urlSuffix: 'api/v4'
}
}
}
};
// Check the endpoints and turn off any options for endpoints that are already registered
this.endpointsService.endpoints$.pipe(first()).subscribe(eps => {
Object.values(this.gitTypes[this.epSubType].types).forEach(type => {
type.exists = !type.url ? false : !!Object.values(eps).find(ep => type.url === getFullEndpointApiUrl(ep));
});
this.init();
});
}
private init() {
// Find first type that is enabled
const defaultSelection = Object.keys(this.gitTypes[this.epSubType].types).find(key => {
const item = this.gitTypes[this.epSubType].types[key];
return !item.exists;
});
this.registerForm = this.fb.group({
selectedType: [defaultSelection, []],
nameField: ['', [Validators.required]],
urlField: ['', [Validators.required]],
skipSllField: [false, []],
createSystemEndpointField: [true, []],
});
this.updateType();
// Check for changes to the from selected type
this.sub = this.registerForm.controls.selectedType.valueChanges.subscribe(changes => this.updateType(changes));
this.validate = this.registerForm.statusChanges.pipe(map(() => {
const typ = this.registerForm.value.selectedType;
const defn = this.gitTypes[this.epSubType].types[typ];
return !!defn.url || this.registerForm.valid;
}));
// Ensure the form validity is updates once the dust settles
setTimeout(() => this.registerForm.updateValueAndValidity(), 0);
}
private updateType(value?: string) {
const typ = value || this.registerForm.value.selectedType;
const defn = this.gitTypes[this.epSubType].types[typ];
this.showEndpointFields = !defn.url;
const entityDefn = entityCatalog.getEndpoint(GIT_ENDPOINT_TYPE, this.epSubType);
this.urlValidation = entityDefn.definition?.urlValidationRegexString;
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
// Perform the endpoint registration
onNext: StepOnNextFunction = () => {
const typ = this.registerForm.value.selectedType;
const defn = this.gitTypes[this.epSubType].types[typ];
const name = defn.name || this.registerForm.controls.nameField.value;
const url: string = this.updateUrlWithSuffix(defn.url || this.registerForm.controls.urlField.value, defn);
// If we're in enterprise mode also assign the skipSSL field, otherwise assume false
const skipSSL = this.registerForm.controls.nameField.value && this.registerForm.controls.urlField.value ?
this.registerForm.controls.skipSllField.value :
false;
const createSystemEndpoint = this.registerForm.controls.createSystemEndpointField.value;
return stratosEntityCatalog.endpoint.api.register<ActionState>(GIT_ENDPOINT_TYPE,
this.epSubType, name, url, skipSSL, '', '', false, createSystemEndpoint)
.pipe(
pairwise(),
filter(([oldVal, newVal]) => (oldVal.busy && !newVal.busy)),
map(([, newVal]) => newVal),
map(result => {
const data: ConnectEndpointConfig = {
guid: result.message,
name,
type: GIT_ENDPOINT_TYPE,
subType: this.epSubType,
ssoAllowed: false
};
if (!result.error) {
this.snackBarService.show(`Successfully registered '${name}'`);
}
const success = !result.error;
return {
success,
redirect: false,
message: success ? '' : result.message,
data
};
})
);
};
private updateUrlWithSuffix(url: string, defn: GithubType): string {
const urlTrimmed = url.trim();
if (!defn.urlSuffix) {
return urlTrimmed;
}
const ready = urlTrimmed[urlTrimmed.length - 1] === '/' ? urlTrimmed.substring(0, urlTrimmed.length - 1) : urlTrimmed;
return ready + '/' + defn.urlSuffix;
}
toggleCreateSystemEndpoint() {
// wait a tick for validators to adjust to new data in the directive
setTimeout(() => {
this.registerForm.controls.nameField.updateValueAndValidity();
this.registerForm.controls.urlField.updateValueAndValidity();
});
}
}