Missed Team ’24? Catch up on announcements here.

×
Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Reading a file from a repository in Bitbucket using the REST API

 

We are currently running an EAP for Forge for Bitbucket, if you want to join the community group send us a request to join  here: https://community.atlassian.com/t5/Bitbucket-Cloud-x-Forge-EAP/gh-p/bitbucket-cloud-forge

I will approve your request right away (Unless I'm sleeping and you'll need to wait a few hours).

If you just want to read more about the EAP, head here: Announcing the start of the Bitbucket Cloud x Forge EAP 

 


 

If you are working on automating a workflow for your team or enhancing the functionality of a Git repository, it is very likely you’ll need a configuration file and that you’ll store it in the Git repo itself.

What about reading that file so that your app can leverage that configuration? How is that done?

To read files from Bitbucket you can use the GET Get file or directory contents REST API in a Forge app. In this article I will show an example of how to read a file and point you in the right direction and hopefully answer some questions upfront.

 

The required parameters

Let’s start listing the required parameters for the endpoint:

  • commit

  • path

  • repo_slug

  • workspace

 

The path, repo_slug and workspace parameters

Path is self-explanatory and, for a configuration file, you will want to save the file at the root level so the parameter is just the filename. Repo_slug and workspace might lead someone to think that you’ll need to retrieve the slug and the name of the workspace, however the UUID are perfectly fine in this - and all other - endpoints as covered in this article.

The commit parameter

Commit instead might not be straightforward and requires some logic to be added even for the simplest case, which is to retrieve the head of a repository. In my experience, when retrieving configuration files, you will almost always want to retrieve the most recent version of a file on the main branch.

All what you need in this case is the name of the default branch and you can simply use that as commit parameter.

Using Forge, this can be done by retrieving the repository calling a function:

const repository = await fetchRepository(event.workspace.uuid, event.repository.uuid);

The function is defined as:

const fetchRepository = async (workspaceUuid, repoUuid) => {
    const res = await api
      .asApp()
      .requestBitbucket(route`/2.0/repositories/${workspaceUuid}/${repoUuid}`);
  
    const data = await res.json();
    return data;
};

Note: In the example above, the worspaceUuid and the repoUuid are retrieved from an event trigger. As alternative you could be using the equivalent useProductContext() for UI Kit or getContext() when using customUI.

The repository default branch is the repository.mainbranch.name property.

 

n my app, this is how I’m fetching the file:

const file = await fetchFile(event.workspace.uuid, event.repository.uuid, repository.mainbranch.name, path);

 

Where the function is defined like this:

const fetchFile = async (workspaceUuid, repoUuid, commit, path) => {
    try {
        const res = await api
        .asApp()
        .requestBitbucket(route`/2.0/repositories/${workspaceUuid}/${repoUuid}/src/${commit}/${path}`);

        if (res.status === 200) {
            const text = await res.text(); 
            return text;
        } else if (res.status === 404) {
            throw new Error('Configuration file not found');
        }
        throw new Error('Unexpected response status');
    } catch (error) {
        throw error;
    }
};

A configuration file might not always be present in a repository, simply because the logic doesn’t apply to all repositories. Because apps are installed at the workspace level, it is important to account for those cases where the configuration file is missing.

A good option for that is to throw an error and to manage it when calling the function in a try…catch statement:

try {
        const file = await fetchFile(event.workspace.uuid, event.repository.uuid, repository.mainbranch.name, '.reviewers');
        
        # the rest of your code

    } catch (error) {
        if (error.message === 'Configuration file not found') {
            // Handle 404 response
            console.log('Configuration file not found');
            } else {
            // Handle other errors
            console.error('Error:', error.message);
        }
    }

How to process the file?

Depending on the file type that you are reading, you’ll need to adjust the logic to read the information that are needed.

YAML files

These files are often chosen for repository configuration because, among other reasons, they are structured and allow for comments to be added.

For a file structured in the following way with a list of reviewers:

reviewers:
- 557058:7f3d31ca-18f9-49ae-9cfd-5b92604de441 #caterina test account1
- 812921:244ed3f9-36db-4c49-b5a9-10d931cad381 #caterina test account2

The Forge app to read the file will have to:

import yaml from 'js-yaml';
....

# load the file
const obj = yaml.load(file);

# access the reviewers array
let reviewers = obj.reviewers;

 

Other file types

Of course, any file type can be read including comma separated and line separated.

Just as an example, this is how you could read a file line by line:

var obj = file.split(/\r\n|\r|\n/);
console.log(`obj.length: ${obj.length}`);

for (var i = 0; i < obj.length; i++) {
     console.log(`file: line ${i} - content ${arr[i]}`);// Do something with arr
}

 

Want to check a real-life example? New to Forge?

If you would like to try this logic out in a fully working app, I’ve created this one for assigning reviewers automatically when creating a PR. The list of reviewers is in defined in each repository via a configuration file that the apps read.

And if you want to know more, check out the documentation and the getting started example.

 

0 comments

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events