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

feat: add gitlab provider #15

Merged
merged 2 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions src/features/readme/generateReadme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ ${installCommand} # Install dependencies${

fs.writeFile(readmeFilename, readmeContent)

// eslint-disable-next-line no-console
console.log()
// eslint-disable-next-line no-console
console.log(readmeContent)

if (created) {
Expand Down
4 changes: 2 additions & 2 deletions src/services/__mocks__/mockFetch.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const mockFetch = (url: string, status: number, data: object = {}) => {
const mockFetch = (referenceUrl: string, status: number, data: object = {}) => {
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn((url: string) =>
Promise.resolve({
status,
json: () => {
if (url === url) {
if (url === referenceUrl) {
return Promise.resolve(data)
}
return Promise.resolve({ data: 100 })
Expand Down
126 changes: 126 additions & 0 deletions src/services/git/__tests__/gitlab.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import mockOrganization from '../../../types/__mocks__/mockOrganization'
import mockRepository from '../../../types/__mocks__/mockRepository'
import mockFetch from '../../__mocks__/mockFetch'
import gitlab from '../gitlab'

describe('gitlab', () => {
describe('isProvider', () => {
it('should always return false', () => {
const provider = gitlab.isProvider('foo')

expect(provider).toEqual(false)
})

it('should always return truen with gitlab ssh url', () => {
const sshUrl = 'git@gitlab.com:friedrith/contributing-generator.git'

const provider = gitlab.isProvider(sshUrl)

expect(provider).toEqual(true)
})

it('should always return truen with gitlab https url', () => {
const httpsUrl = 'https://gitlab.com/friedrith/contributing-generator.git'

const provider = gitlab.isProvider(httpsUrl)

expect(provider).toEqual(true)
})
})

describe('getProviderName', () => {
it('should return gitlab', () => {
const providerName = gitlab.getProviderName()

expect(providerName).toEqual('gitlab')
})
})

describe('findOrganization', () => {
it('should return default organization when 404', async () => {
mockFetch('https://gitlab.com/api/v4/users?username=friedrith', 404)
const url = 'git@gitlab.com:friedrith/contributing-generator.git'

const organization = await gitlab.findOrganization(url)

expect(organization).toEqual({
username: 'friedrith',
name: 'friedrith',
email: undefined,
})
})

it('should return organization with ssh url', async () => {
mockFetch('https://gitlab.com/api/v4/users?username=friedrith', 200, [
{
name: 'Thibault Friedrich',
email: null,
},
])

const url = 'git@gitlab.com:friedrith/contributing-generator.git'

const organization = await gitlab.findOrganization(url)

expect(organization).toEqual({
username: 'friedrith',
name: 'Thibault Friedrich',
email: null,
})
})

it('should return organization with http url', async () => {
mockFetch('https://gitlab.com/api/v4/users?username=friedrith', 200, [
{
name: 'Thibault Friedrich',
email: null,
},
])

const url = 'https://gitlab.com/friedrith/contributing-generator.git'

const organization = await gitlab.findOrganization(url)

expect(organization).toEqual({
username: 'friedrith',
name: 'Thibault Friedrich',
email: null,
})
})
})

describe('getIssueTrackerUrl', () => {
it('should return issue tracker url', async () => {
const organization = mockOrganization({ username: 'foo' })
const repository = mockRepository({ name: 'bar' })
const url = await gitlab.getIssueTrackerUrl(organization, repository)

expect(url).toEqual('https://gitlab.com/foo/bar/-/issues')
})
})

describe('getRepositoryInformation', () => {
it('should return empty string when 404', async () => {
mockFetch('https://gitlab.com/api/v4/users', 404)

const username = 'username'
const name = 'repo'
const repository = await gitlab.getRepositoryInformation(username, name)

expect(repository).toEqual({})
})

it('should return empty string when 404', async () => {
mockFetch('https://api.gitlab.com/repos/username/repo', 200, {
description: 'foo',
topics: ['bar', 'baz'],
})

const username = 'username'
const name = 'repo'
const repository = await gitlab.getRepositoryInformation(username, name)

expect(repository).toEqual({})
})
})
})
18 changes: 18 additions & 0 deletions src/services/git/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ describe('git', () => {
})
})

it('should return gitlab organization', async () => {
mockFetch('https://gitlab.com/api/v4/users?username=friedrith', 200, [
{
name: 'Thibault Friedrich',
email: null,
},
])
const url = 'git@gitlab.com:friedrith/contributing-generator.git'

const organization = await git.findOrganization(url)

expect(organization).toEqual({
email: null,
name: 'Thibault Friedrich',
username: 'friedrith',
})
})

it('should return default organization', async () => {
mockFetch('https://github.com/gitapi/users/friedrith', 200, {
name: 'Thibault Friedrich',
Expand Down
71 changes: 71 additions & 0 deletions src/services/git/gitlab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Organization from '../../types/Organization'
import Project from '../../types/Project'
import Repository from '../../types/Repository'
import GitProvider from './types/GitProvider'

const getGitlabUser = async (username: string) => {
try {
const url = new URL('https://gitlab.com/api/v4/users')
url.searchParams.append('username', username)

const response = await fetch(url.toString())

if (response.status === 404) {
return {}
}

const user: { name: string; email: string } = (await response.json())[0]

return {
name: user.name,
email: user.email,
}
} catch (error) {
return {}
}
}

const HTTP_HOST = 'https://gitlab.com'

const isProvider = (url: string) =>
url.startsWith('git@gitlab.com:') || url.startsWith(HTTP_HOST)

const getProviderName = () => 'gitlab'

const getIssueTrackerUrl = async (
organization: Organization,
repository: Repository,
) => `https://gitlab.com/${organization.username}/${repository.name}/-/issues`

const findOrganization = async (url: string): Promise<Organization> => {
const username = url.startsWith(HTTP_HOST)
? url.replace(HTTP_HOST, '').split('/')[1]
: url.split(':')[1].split('/')[0]

const user = await getGitlabUser(username)

return {
username,
name: user?.name ?? username,
email: user?.email,
}
}

const getRepositoryInformation = async (
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_username: string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_name: string,
): Promise<Partial<Project>> => {
// it seems gitlab doesn't provide any description or keywords
// https://docs.gitlab.com/ee/api/repositories.html
return {}
}

export default {
isProvider,
getProviderName,
findOrganization,
getIssueTrackerUrl,
getRepositoryInformation,
} satisfies GitProvider
3 changes: 2 additions & 1 deletion src/services/git/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Repository from '../../types/Repository'
import Project from '../../types/Project'
import GitProvider from './types/GitProvider'
import defaultProvider from './defaultProvider'
import gitlab from './gitlab'

const executeCommand = async (command: string, args: string[]) =>
(await util.promisify(execFile)(command, args)).stdout.replace('\n', '')
Expand All @@ -23,7 +24,7 @@ export const findEmail = async () =>
export const findName = async () =>
executeCommand('git', ['config', 'user.name'])

const providers = [github, defaultProvider]
const providers = [github, gitlab, defaultProvider]

const findProvider = (url: string): GitProvider =>
providers.find(provider => provider.isProvider(url))
Expand Down