I have a project in Azure Devops with underlying repository as git. I have a automatically created database documentation which is stored in the project repository. To keep this documentation up-to date I want to schedule an application to push the generated documentation to azure on daily basis.
Basically, check out the file, write new content & check-in. Can we do this using Azure devops rest APIs? Is there any example code that I can follow?
You can have a scheduled build using Azure Pipelines, and in the build definition you define a Powershell script that run git related git commands as ShaykiAbramczyk suggested.
Need pay attention with below if you want to run Git commands in a script:
Grant version control permissions to the build service
Allow scripts to access the system token
Merge a feature branch to master
More details please refer our official doc here-- Run Git commands in a script
Script snippet:
#Config Set
git config user.email "$(Build.RequestedForEmail)"
git config user.name "$(Build.RequestedFor)"
#Push new Branch
git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" push origin master:refs/heads/my-branch
#Other command
......
This is how I have implemented the a solution to check-in content in Azure Devops Git Repo.
Below is the generic class & caller method.
class AzureDevops
{
private readonly Uri uri;
private readonly string personalAccessToken;
public AzureDevops(string orgName, string personalAccessToken)
{
this.uri = new Uri("https://dev.azure.com/" + orgName);
this.personalAccessToken = personalAccessToken;
}
public T Post<T>(dynamic body, string path)
{
if (body == null)
throw new ArgumentNullException("body");
if (path == null)
throw new ArgumentNullException("path");
T output = default(T);
using (var client = new HttpClient())
{
client.BaseAddress = uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", personalAccessToken);
string serl_body = JsonConvert.SerializeObject(body);
var content = new StringContent(serl_body, Encoding.UTF8, "application/json");
using (HttpResponseMessage response = client.PostAsync(path, content).Result)
{
response.EnsureSuccessStatusCode();
output = response.Content.ReadAsAsync<T>().Result;
}
}
return output;
}
public T Get<T>(string path)
{
if (path == null)
throw new ArgumentNullException("path");
T output = default(T);
using (var client = new HttpClient())
{
client.BaseAddress = uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", personalAccessToken);
HttpResponseMessage response = client.GetAsync(path).Result;
if (response.IsSuccessStatusCode)
output = response.Content.ReadAsAsync<T>().Result;
else
throw new ApplicationException(string.Format("Response message is not OK. Issues in action: {0}", path));
}
return output;
}
}
public class Main
{
AzureDevops azureDevops = new AzureDevops("OrgName", "PAT");
private void AddNewContent()
{
ListOfRefResponse.Root listOfRefResponse = azureDevops.Get<ListOfRefResponse.Root>(string.Format("{0}/_apis/git/repositories/{1}/refs? api-version=6.0-preview.1&filter=heads/master", "projectId", "repositoryId"));
ArrayList contentArray = new ArrayList();
contentArray.Add(new ChangesBO
{
changeType = "add",
item = new ChangeItemBO { path = string.Concat("/", Constants.BaseAzureFolder, "/", "projedctName" + "/" + "filename.md") },
newContent = new ChangeContent { content = "new text content", contentType = "rawtext" }
});
dynamic body = new
{
refUpdates = new[] { new { name = Constants.Branch, oldObjectId = listOfRefResponse.value.First().objectId } },
commits = new[] {
new {
comment = Constants.AppKeysUpdateComment,
changes = contentArray.ToArray()
}}
};
CommitSuccessBO.Root commitSuccess = azureDevops.Post<CommitSuccessBO.Root>(body, string.Format("_apis/git/repositories/{0}/pushes?api-version=5.0", "RepositoryId"));
}
}
Related
I have a Azure blob container which I am accessing using below code -
var blobContainerClient = GetBlobContainer(containerName);
if (blobContainerClient != null)
{
// List all blobs in the container
await foreach (BlobItem blobItem in blobContainerClient.GetBlobsAsync())
{
queuedBlobsList.Add(new QueuedBlobs { BlobName = blobItem.Name, LastModified = blobItem.Properties.LastModified });
}
}
private BlobContainerClient GetBlobContainer(string containerName)
{
return gen2StorageClient != null
? gen2StorageClient.GetBlobContainerClient(containerName)
: gen1StorageClient.GetBlobContainerClient(containerName);
}
The clients are initialised in constructor -
public class BlobService : IBlobService
{
private readonly BlobServiceClient gen1StorageClient, gen2StorageClient;
public BlobService(BlobServiceClient defaultClient, IAzureClientFactory<BlobServiceClient> clientFactory)
{
gen1StorageClient = defaultClient;
if (clientFactory != null)
{
gen2StorageClient = clientFactory.CreateClient("StorageConnectionString");
}
}
}
And my unit test where I am setting GetBlobsAsync is like this -
But I want to add list of BlobItems to test another loop.
private static Mock<BlobContainerClient> GetBlobContainerClientMockWithListOfBlobs()
{
var blobContainerClientMock = new Mock<BlobContainerClient>("UseDevelopmentStorage=true", EnvironmentConstants.ParallelUploadContainer);
var cancellationToken = new CancellationToken();
var blobs = new List<BlobItem>();
//AsyncPageable<BlobItem> blobItems = new AsyncPageable<BlobItem>(); -- Not allowing
blobContainerClientMock.Setup(x => x.GetBlobsAsync(BlobTraits.All, BlobStates.All, null, cancellationToken)).Returns(It.IsAny<AsyncPageable<BlobItem>>());
return blobContainerClientMock;
}
I came to this question because I also had the same issue.
Based on this article
AsyncPageable<T> and Pageable<T> are classes that represent collections of models returned by the service in pages.
The method GetBlobsAsync returns an AsyncPageable.
To Create an AsyncPageable you need first to create a BlobItem Page.
To create a Page<T> instance, use the Page<T>.FromValues method, passing a list of items, a continuation token, and the Response.
So let's start creating the list of items:
var blobList = new BlobItem[]
{
BlobsModelFactory.BlobItem("Blob1"),
BlobsModelFactory.BlobItem("Blob2"),
BlobsModelFactory.BlobItem("Blob3")
};
Note: BlobItem has an internal constructor but I found in this answer that there's a BlobsModelFactory.
After having the list of blobs is time to create a Page<BlobItem>:
Page<BlobItem> page = Page<BlobItem>.FromValues(blobList, null, Mock.Of<Response>());
And finally, create the AsyncPageable<BlobItem>
AsyncPageable<BlobItem> pageableBlobList = AsyncPageable<BlobItem>.FromPages(new[] { page });
And now you are able to use this to mock GetBlobsAsync method:
blobContainerClientMock
.Setup(m => m.GetBlobsAsync(
It.IsAny<BlobTraits>(),
It.IsAny<BlobStates>(),
It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.Returns(pageableBlobList);
I hope this helps others with this issue.
André
I am trying to download a zip archive from Azure via REST API using this Request Url and the C# code below. The response StatusCode is 200 so I assume it is working. But the zip file does not get downloaded to my computer. What am I doing wrong?
GET https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repositoryId}/items?path={path}&scopePath={scopePath}&recursionLevel={recursionLevel}&includeContentMetadata={includeContentMetadata}&latestProcessedChange={latestProcessedChange}&download={download}&$format={$format}&versionDescriptor.version={versionDescriptor.version}&versionDescriptor.versionOptions={versionDescriptor.versionOptions}&versionDescriptor.versionType={versionDescriptor.versionType}&includeContent={includeContent}&resolveLfs={resolveLfs}&sanitize={sanitize}&api-version=6.0
public static void DownloadTest()
{
var repository = "myrepository";
var branch = "main";
var url = $"https://dev.azure.com/{Constants.ORGANIZATION}/{Constants.PROJECT}/_apis/git/repositories/{repository}/items?path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=0&versionDescriptor[version]={branch}&resolveLfs=true&$format=zip&api-version=6.0&download=true";
try
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/zip"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "", Constants.PAT))));
var response = httpClient.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I am using Azure Automation Management 2.0.1. I am not able find Start method for Runbooks to execute a runbook. How do this with 2.0.1
var client = new Microsoft.Azure.Management.Automation.AutomationManagementClient(new CertificateCloudCredentials(subscriptionId, cert));
var ct = new CancellationToken();
var content = await client.Runbooks.ListByNameAsync("MyAutomationAccountName", "MyRunbookName", ct);
var firstOrDefault = content?.Runbooks.FirstOrDefault();
if (firstOrDefault != null)
{
var operation = client.Runbooks.Start("MyAutomationAccountName", new RunbookStartParameters(firstOrDefault.Id));
}
You need to use the automationManagementClient.Jobs.Create
public static JobCreateResponse Create(
this IJobOperations operations,
string resourceGroupName,
string automationAccount,
JobCreateParameters parameters
)
You can find a full sample here this would be the relevant part -
private void JobStart_Click(object sender, RoutedEventArgs e)
{
// Check for runbook name
if (String.IsNullOrWhiteSpace(RunbookName.Text) || String.IsNullOrWhiteSpace(PublishState.Text)) throw new ArgumentNullException(RunbookName.Text);
// Create job create parameters
var jcparam = new JobCreateParameters
{
Properties = new JobCreateProperties
{
Runbook = new RunbookAssociationProperty
{
// associate the runbook name
Name = RunbookName.Text
},
// pass parameters to runbook if any
Parameters = null
}
};
// create runbook job. This gives back JobId
var job = automationManagementClient.Jobs.Create(this.automationAccountName, jcparam).Job;
JobGuid.Text = JobId.Text = job.Properties.JobId.ToString();
Log.Text += (String.Format("\nJob Started for Runbook {0} , JobId {1}", RunbookName.Text, JobId.Text));
}
In my project I am supposed to get data from openweathermap.org and put that in a collection in my DocumentDB database in Azure.
The code below works locally on my development machine, but when i upload the project, it runs and succeed (says the dashboard) but no documents are created. I can only create the documents if I run from local machine.
Why is that?
Here is my code:
public static void Main()
{
JobHost host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.Call(typeof(Program).GetMethod("saveWeatherDataToAzureDocumentDB"));
}
[NoAutomaticTrigger]
public static async void saveWeatherDataToAzureDocumentDB()
{
string endpointUrl = ConfigurationManager.AppSettings["EndPointUrl"];
string authorizationKey = ConfigurationManager.AppSettings["AuthorizationKey"];
string url = "http://api.openweathermap.org/data/2.5/weather?q=hanstholm,dk&appid=44db6a862fba0b067b1930da0d769e98";
var request = WebRequest.Create(url);
string text;
var response = (HttpWebResponse)request.GetResponse();
using (var sr = new StreamReader(response.GetResponseStream()))
{
text = sr.ReadToEnd();
}
// Create a new instance of the DocumentClient
var client = new DocumentClient(new Uri(endpointUrl), authorizationKey);
// Check to verify a database with the id=FamilyRegistry does not exist
Database database = client.CreateDatabaseQuery().Where(db => db.Id == "weatherdata").AsEnumerable().FirstOrDefault();
// If the database does not exist, create a new database
if (database == null)
{
database = await client.CreateDatabaseAsync(
new Database
{
Id = "weatherdata"
});
}
// Check to verify a document collection with the id=FamilyCollection does not exist
DocumentCollection documentCollection = client.CreateDocumentCollectionQuery(database.SelfLink).Where(c => c.Id == "weathercollection").AsEnumerable().FirstOrDefault();
// If the document collection does not exist, create a new collection
if (documentCollection == null)
{
documentCollection = await client.CreateDocumentCollectionAsync("dbs/" + database.Id,
new DocumentCollection
{
Id = "weathercollection"
});
}
//Deserialiser til et dynamisk object
if (text == "")
{
mark m = new mark() { name = "Something" };
await client.CreateDocumentAsync(documentCollection.DocumentsLink, m);
}
else
{
var json = JsonConvert.DeserializeObject<dynamic>(text);
json["id"] = json["name"] + "_" + DateTime.Now;
await client.CreateDocumentAsync(documentCollection.DocumentsLink, json);
}
}
public sealed class mark
{
public string name { get; set; }
}
UPDATE - This is what I have in my App.config
<appSettings>
<!-- Replace the value with the value you copied from the Azure management portal -->
<add key="EndPointUrl" value="https://<My account>.documents.azure.com:443/"/>
<!-- Replace the value with the value you copied from the Azure management portal -->
<add key="AuthorizationKey" value="The secret code from Azure"/>
Also, At DocumentDB Account i find the Connection string like this. AccountEndpoint=https://knoerregaard.documents.azure.com:443/;AccountKey=my secret password
How should I apply this to the WebJob?
Appriciate your help!
I want to post some request values alongside the multipart-formdata file contents. In the old API you could use PostFileWithRequest:
[Test]
public void Can_POST_upload_file_using_ServiceClient_with_request()
{
IServiceClient client = new JsonServiceClient(ListeningOn);
var uploadFile = new FileInfo("~/TestExistingDir/upload.html".MapProjectPath());
var request = new FileUpload{CustomerId = 123, CustomerName = "Foo"};
var response = client.PostFileWithRequest<FileUploadResponse>(ListeningOn + "/fileuploads", uploadFile, request);
var expectedContents = new StreamReader(uploadFile.OpenRead()).ReadToEnd();
Assert.That(response.FileName, Is.EqualTo(uploadFile.Name));
Assert.That(response.ContentLength, Is.EqualTo(uploadFile.Length));
Assert.That(response.Contents, Is.EqualTo(expectedContents));
Assert.That(response.CustomerName, Is.EqualTo("Foo"));
Assert.That(response.CustomerId, Is.EqualTo(123));
}
I can't find any such method in the new API, nor any overrides on client.Post() which suggest that this is still possible. Does anyone know if this is a feature that was dropped?
Update
As #Mythz points out, the feature wasn't dropped. I had made the mistake of not casting the client:
private IRestClient CreateRestClient()
{
return new JsonServiceClient(WebServiceHostUrl);
}
[Test]
public void Can_WebRequest_POST_upload_binary_file_to_save_new_file()
{
var restClient = (JsonServiceClient)CreateRestClient(); // this cast was missing
var fileToUpload = new FileInfo(#"D:/test/test.avi");
var beforeHash = this.Hash(fileToUpload);
var response = restClient.PostFileWithRequest<FilesResponse>("files/UploadedFiles/", fileToUpload, new TestRequest() { Echo = "Test"});
var uploadedFile = new FileInfo(FilesRootDir + "UploadedFiles/test.avi");
var afterHash = this.Hash(uploadedFile);
Assert.That(beforeHas, Is.EqualTo(afterHash));
}
private string Hash(FileInfo file)
{
using (var md5 = MD5.Create())
{
using (var stream = file.OpenRead())
{
var bytes = md5.ComputeHash(stream);
return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLower();
}
}
}
None of the old API was removed from the C# Service Clients, only new API's were added.
The way you process an uploaded file inside a service also hasn't changed.