@@ -3,10 +3,10 @@ import { head } from 'ramda';
33
44import { Row } from '@/constants/db' ;
55import { Integration } from '@/constants/integrations' ;
6- import { BaseRepo } from '@/types/resources' ;
7- import { db , getFirstRow } from '@/utils/db' ;
86import { DEFAULT_GH_URL } from '@/constants/urls' ;
7+ import { BaseRepo } from '@/types/resources' ;
98import { dec } from '@/utils/auth-supplementary' ;
9+ import { db , getFirstRow } from '@/utils/db' ;
1010
1111type GithubRepo = {
1212 name : string ;
@@ -271,6 +271,134 @@ export const gitlabSearch = async (pat: string, searchString: string) => {
271271 return searchGitlabRepos ( pat , search ) ;
272272} ;
273273
274+ // Bitbucket functions
275+
276+ type BitbucketRepo = {
277+ uuid : string ;
278+ name : string ;
279+ full_name : string ;
280+ description ?: string ;
281+ language ?: string ;
282+ mainbranch ?: {
283+ name : string ;
284+ } ;
285+ links : {
286+ html : {
287+ href : string ;
288+ } ;
289+ } ;
290+ owner : {
291+ username : string ;
292+ } ;
293+ } ;
294+
295+ type BitbucketResponse = {
296+ values : BitbucketRepo [ ] ;
297+ next ?: string ;
298+ } ;
299+
300+ const BITBUCKET_API_URL = 'https://api.bitbucket.org/2.0' ;
301+
302+ export const searchBitbucketRepos = async (
303+ credentials : string ,
304+ searchString : string
305+ ) : Promise < BaseRepo [ ] > => {
306+ let urlString = convertUrlToQuery ( searchString ) ;
307+ if ( urlString !== searchString && urlString . includes ( '/' ) ) {
308+ try {
309+ return await searchBitbucketRepoWithURL ( credentials , urlString ) ;
310+ } catch ( e ) {
311+ return await searchBitbucketReposWithNames ( credentials , urlString ) ;
312+ }
313+ }
314+ return await searchBitbucketReposWithNames ( credentials , urlString ) ;
315+ } ;
316+
317+ const searchBitbucketRepoWithURL = async (
318+ credentials : string ,
319+ searchString : string
320+ ) : Promise < BaseRepo [ ] > => {
321+ const apiUrl = `${ BITBUCKET_API_URL } /repositories/${ searchString } ` ;
322+
323+ const response = await fetch ( apiUrl , {
324+ method : 'GET' ,
325+ headers : {
326+ Authorization : `Basic ${ credentials } ` ,
327+ 'Content-Type' : 'application/json'
328+ }
329+ } ) ;
330+
331+ if ( ! response . ok ) {
332+ throw new Error ( `Bitbucket API error: ${ response . statusText } ` ) ;
333+ }
334+
335+ const repo = ( await response . json ( ) ) as BitbucketRepo ;
336+
337+ return [
338+ {
339+ id : repo . uuid . replace ( / [ { } ] / g, '' ) ,
340+ name : repo . name ,
341+ desc : repo . description ,
342+ slug : repo . name ,
343+ parent : repo . owner . username ,
344+ web_url : repo . links . html . href ,
345+ branch : repo . mainbranch ?. name ,
346+ language : repo . language ,
347+ provider : Integration . BITBUCKET
348+ }
349+ ] as BaseRepo [ ] ;
350+ } ;
351+
352+ const searchBitbucketReposWithNames = async (
353+ credentials : string ,
354+ searchString : string
355+ ) : Promise < BaseRepo [ ] > => {
356+ const apiUrl = `${ BITBUCKET_API_URL } /repositories` ;
357+ const params = new URLSearchParams ( {
358+ q : `name~"${ searchString } "` ,
359+ role : 'member' ,
360+ pagelen : '50'
361+ } ) ;
362+
363+ const response = await fetch ( `${ apiUrl } ?${ params } ` , {
364+ method : 'GET' ,
365+ headers : {
366+ Authorization : `Basic ${ credentials } ` ,
367+ 'Content-Type' : 'application/json'
368+ }
369+ } ) ;
370+
371+ if ( ! response . ok ) {
372+ throw new Error ( `Bitbucket API error: ${ response . statusText } ` ) ;
373+ }
374+
375+ const responseBody = ( await response . json ( ) ) as BitbucketResponse ;
376+ const repositories = responseBody . values || [ ] ;
377+
378+ return repositories . map (
379+ ( repo ) =>
380+ ( {
381+ id : repo . uuid . replace ( / [ { } ] / g, '' ) ,
382+ name : repo . name ,
383+ desc : repo . description ,
384+ slug : repo . name ,
385+ parent : repo . owner . username ,
386+ web_url : repo . links . html . href ,
387+ branch : repo . mainbranch ?. name ,
388+ language : repo . language || null ,
389+ provider : Integration . BITBUCKET
390+ } ) as BaseRepo
391+ ) ;
392+ } ;
393+
394+ export const bitbucketSearch = async (
395+ credentials : string ,
396+ searchString : string
397+ ) : Promise < BaseRepo [ ] > => {
398+ let search = convertUrlToQuery ( searchString ) ;
399+ return searchBitbucketRepos ( credentials , search ) ;
400+ } ;
401+
274402const convertUrlToQuery = ( url : string ) => {
275403 let query = url ;
276404 try {
@@ -282,6 +410,7 @@ const convertUrlToQuery = (url: string) => {
282410 query = query . replace ( 'http://' , '' ) ;
283411 query = query . replace ( 'github.com/' , '' ) ;
284412 query = query . replace ( 'gitlab.com/' , '' ) ;
413+ query = query . replace ( 'bitbucket.org/' , '' ) ;
285414 query = query . startsWith ( 'www.' ) ? query . slice ( 4 ) : query ;
286415 query = query . endsWith ( '/' ) ? query . slice ( 0 , - 1 ) : query ;
287416 }
@@ -315,26 +444,27 @@ export const getGitHubCustomDomain = async (): Promise<string | null> => {
315444
316445 return head ( provider_meta || [ ] ) ?. custom_domain || null ;
317446 } catch ( error ) {
318- console . error ( 'Error occured while getting custom domain from database:' , error ) ;
447+ console . error (
448+ 'Error occured while getting custom domain from database:' ,
449+ error
450+ ) ;
319451 return null ;
320452 }
321453} ;
322454
323- const normalizeSlashes = ( url : string ) =>
324- url . replace ( / (?< ! : ) \/ { 2 , } / g, '/' ) ;
455+ const normalizeSlashes = ( url : string ) => url . replace ( / (?< ! : ) \/ { 2 , } / g, '/' ) ;
325456
326457export const getGitHubRestApiUrl = async ( path : string ) => {
327458 const customDomain = await getGitHubCustomDomain ( ) ;
328- const base = customDomain
329- ? `${ customDomain } /api/v3`
330- : DEFAULT_GH_URL ;
459+ const base = customDomain ? `${ customDomain } /api/v3` : DEFAULT_GH_URL ;
331460 return normalizeSlashes ( `${ base } /${ path } ` ) ;
332461} ;
333462
334-
335463export const getGitHubGraphQLUrl = async ( ) : Promise < string > => {
336464 const customDomain = await getGitHubCustomDomain ( ) ;
337- return customDomain ? `${ customDomain } /api/graphql` : `${ DEFAULT_GH_URL } /graphql` ;
465+ return customDomain
466+ ? `${ customDomain } /api/graphql`
467+ : `${ DEFAULT_GH_URL } /graphql` ;
338468} ;
339469
340470export const getGithubToken = async ( org_id : ID ) => {
0 commit comments