如何将附件添加,使用拉力REST .NET用户故事拉力、如何将、附件、故事

2023-09-05 04:33:49 作者:假面

我们是从SOAP移植我们的.NET拉力code到REST .NET API的过程。到目前为止好,对REST API似乎更快,更容易使用,因为没有WSDL打破每次工作产品的自定义字段中的拉力赛工作区更改。

我有一件事烦恼,虽然我们试图复制到上传附件的能力。我们一直遵循一个非常类似的过程,以在本贴中概述了一句:

Rally SOAP API - 我如何添加附件以分层要求

由此图像被读入一个System.Drawing.Image对象。我们使用ImageToByteArray功能将图像转换到哪个然后被分配给AttachmentContent,这是首先创建一个字节数组。

然后,附件被创建,和有线到既AttachmentContent和HierarchicalRequirement

所有的创作活动的工作带来极大的。内容对象被创建的罚款。然后新附件称为Image.png被创建并链接到故事。但是,当我从拉力赛下载所产生的依恋,Image.png具有零字节!我一直试图与不同的图像,JPEG的,巴新等,都具有相同的结果。

ç展示我​​们的过程中,$ C $的摘录如下包括在内。有什么明显的是我丢失吗?先谢谢了。

  // ....阅读内容到一个名为imageObject System.Drawing.Image对象....

    //图像转换为字节数组
    byte []的imageBytes = ImageToByteArray(imageObject,System.Drawing.Imaging.ImageFormat.Png);
    VAR imageLength = imageBytes.Length;

    // AttachmentContent
    DynamicJsonObject attachmentContent =新DynamicJsonObject();
    attachmentContent [内容] = imageBytes;

    CreateResult CR = restApi.Create(AttachmentContent,myAttachmentContent);
    字符串contentRef = cr.Reference;
    Console.WriteLine(创:+ contentRef);

    //设置附件
    DynamicJsonObject newAttachment =新DynamicJsonObject();
    newAttachment [神器] =故事;
    newAttachment [内容] = attachmentContent;
    newAttachment [名称] =Image.png;
    newAttachment [的ContentType] =图像/ PNG;
    newAttachment [大小] = imageLength;
    newAttachment [用户] =用户;


    //创建附件拉力赛
    CR = restApi.Create(附件,newAttachment);

    字符串attachRef = cr.Reference;
    Console.WriteLine(创:+ attachRef);

}

公共静态的byte [] ImageToByteArray(图像图像,System.Drawing.Imaging.ImageFormat格式)
{
    使用(MemoryStream的毫秒=新的MemoryStream())
    {
        image.Save(MS,格式);

        //图像转换为byte []
        byte []的imageBytes = ms.ToArray();
        返回imageBytes;
    }
}
 

解决方案 单团队敏捷开发管理示例 leangoo

这个问题也让我疑惑了一会儿 - 终于整理出来大约一个星期前

两个意见:

当拉力赛的SOAP API的字节数组将序列化到后台为Base64字符串,REST并不为你做这一步,将期待为Base64格式的字符串传递的内容属性的AttachmentContent对象。 如你的榜样将不能提供正确的长度拉力赛的WSAPI期待System.Drawing.Image.Length。你需要传递的Base64格式的字符串的长度被转换回常规字符串之后。这也是一样的字节数组的长度。

我包括code示例来说明:

  //系统库
使用系统;
使用System.Collections.Generic;
使用System.Linq的;
使用System.Text;
使用System.Drawing.Imaging;
使用System.Drawing中;
使用System.IO;
使用的System.Web;

//拉力REST API库
使用Rally.RestApi;
使用Rally.RestApi.Response;

命名空间RestExample_CreateAttachment
{
    类节目
    {
        静态无效的主要(字串[] args)
        {
            //设置用户参数
            字符串username =user@company.com;
            字符串的userPassword =密码;

            //设置拉力参数
            字符串rallyURL =htt​​ps://rally1.rallydev.com;
            字符串rallyWSAPIVersion =1.36;

            //初始化REST API
            RallyRestApi RESTAPI;
            RESTAPI =新RallyRestApi(用户名,
                                       userPassword的,
                                       rallyURL,
                                       rallyWSAPIVersion);

            //创建请求用户
            请求userRequest =新的请求(用户);
            userRequest.Fetch =新的名单,其中,串>()
                {
                    用户名,
                    订阅,
                    显示名称,
                };

            //添加一个查询请求
            userRequest.Query =新的查询(用户名,Query.Operator.Equals,用户名);

            //查询拉力赛
            QueryResult中queryUserResults = restApi.Query(userRequest);

            //抓斗导致用户对象和参考
            DynamicJsonObject MYUSER =新DynamicJsonObject();
            MYUSER = queryUserResults.Results.First();
            字符串myUserRef = MYUSER [_参考];

            //设置我们的工作区和项目scopings
            字符串workspaceRef =/工作区/ 12345678910;
            字符串projectRef =/项目/ 12345678911;
            布尔projectScopingUp = FALSE;
            布尔projectScopingDown = TRUE;

            //发现,我们要添加附件用户故事

            //一试身手了故事请求
            请求storyRequest =新的请求(hierarchicalrequirement);
            storyRequest.Workspace = workspaceRef;
            storyRequest.Project = projectRef;
            storyRequest.ProjectScopeDown = projectScopingDown;
            storyRequest.ProjectScopeUp = projectScopingUp;

            //字段取
            storyRequest.Fetch =新的名单,其中,串>()
                {
                    名称,
                    FormattedID
                };

            //添加查询
            storyRequest.Query =新的查询(FormattedID,Query.Operator.Equals,US43);

            //查询拉力赛的故事
            QueryResult中QueryResult中= restApi.Query(storyRequest);

            //拉参考过故事的取
            变种storyObject = queryResult.Results.First();
            字符串storyReference = storyObject [_参考];

            //读取图像内容
            字符串imageFilePath =C:\\用户\\用户名\\;
            字符串映像文件名称=image1.png;
            字符串fullImageFile = imageFilePath +映像文件名称;
            图片MYIMAGE = Image.FromFile(fullImageFile);

            //从Image.Length属性长度 - 不是在做REST,虽然使用
            // REST需要以字节数的图像的长度换算为一个字节数组
            VAR imageFileLength =新的FileInfo(fullImageFile).Length;
            Console.WriteLine(从System.Drawing.Image对象图像文件长度:+ imageFileLength);

            //图像转换为Base64格式
            字符串imageBase64String = ImageToBase64(MYIMAGE,System.Drawing.Imaging.ImageFormat.Png);

            //从Base64String计算长度转换回
            VAR imageNumberBytes = Convert.FromBase64String(imageBase64String).Length;

            //这不同于基64字符串只是长度!
            Console.WriteLine(图像文件长度从Convert.FromBase64String:+ imageNumberBytes);

            // DynamicJSONObject的AttachmentContent
            DynamicJsonObject myAttachmentContent =新DynamicJsonObject();
            myAttachmentContent [内容] = imageBase64String;

            尝试
            {
                CreateResult myAttachmentContentCreateResult = restApi.Create(AttachmentContent,myAttachmentContent);
                字符串myAttachmentContentRef = myAttachmentContentCreateResult.Reference;
                Console.WriteLine(创:+ myAttachmentContentRef);

                // DynamicJSONObject的附件集装箱
                DynamicJsonObject myAttachment =新DynamicJsonObject();
                myAttachment [神器] = storyReference;
                myAttachment [内容] = myAttachmentContentRef;
                myAttachment [名称] =AttachmentFromREST.png;
                myAttachment [说明] =附件说明;
                myAttachment [的ContentType] =图像/ PNG;
                myAttachment [大小] = imageNumberBytes;
                myAttachment [用户] = myUserRef;

                CreateResult myAttachmentCreateResult = restApi.Create(附件,myAttachment);

                名单<字符串> createErrors = myAttachmentContentCreateResult.Errors;
                的for(int i = 0; I< createErrors.Count;我++)
                {
                    Console.WriteLine(createErrors [I]);
                }

                字符串myAttachmentRef = myAttachmentCreateResult.Reference;
                Console.WriteLine(创:+ myAttachmentRef);

            }
            赶上(例外五)
            {
                Console.WriteLine(未处理的异常:+ e.StackTrace);
                Console.WriteLine(e.Message);
            }
        }

        //转换图像库64恩codeD字符串
        公共静态字符串ImageToBase64(图像图像,System.Drawing.Imaging.ImageFormat格式)
        {
            使用(MemoryStream的毫秒=新的MemoryStream())
            {
                image.Save(MS,格式);
                //图像转换为byte []
                byte []的imageBytes = ms.ToArray();

                //转换字节[]为Base64字符串
                字符串base64String = Convert.ToBase64String(imageBytes);

                返回base64String;
            }
        }
    }
}
 

We're in the process of porting our .NET Rally code from SOAP to the REST .NET API. So far so good, the REST API seems to be faster and is easier to use since there's no WSDL to break each time the work product custom fields change in the Rally Workspace.

I'm having trouble with one thing though as we try to replicate the ability to upload attachments. We're following a very similar procedure as to the one outlined in this posting:

Rally SOAP API - How do I add an attachment to a Hierarchical Requirement

Whereby the image is read into a System.Drawing.Image. We use the ImageToByteArray function to convert the image to a byte array which then gets assigned to the AttachmentContent, which is created first.

Then, the Attachment gets created, and wired up to both AttachmentContent, and the HierarchicalRequirement.

All of the creation events work great. The content object gets created fine. Then the new attachment called "Image.png" gets created and linked to the Story. But when I download the resulting attachment from Rally, Image.png has zero bytes! I've tried this with different images, JPEG's, PNG's, etc. all with the same results.

An excerpt of the code showing our process is included below. Is there something obvious that I'm missing? Thanks in advance.

    // .... Read content into a System.Drawing.Image called imageObject ....

    // Convert Image to byte array
    byte[] imageBytes = ImageToByteArray(imageObject, System.Drawing.Imaging.ImageFormat.Png);
    var imageLength = imageBytes.Length;

    // AttachmentContent
    DynamicJsonObject attachmentContent = new DynamicJsonObject();
    attachmentContent["Content"] = imageBytes ;

    CreateResult cr = restApi.Create("AttachmentContent", myAttachmentContent);
    String contentRef = cr.Reference;
    Console.WriteLine("Created: " + contentRef);

    // Set up attachment
    DynamicJsonObject newAttachment = new DynamicJsonObject();
    newAttachment["Artifact"] = story;
    newAttachment["Content"] = attachmentContent;
    newAttachment["Name"] = "Image.png";
    newAttachment["ContentType"] = "image/png";
    newAttachment["Size"] = imageLength;
    newAttachment["User"] = user;


    // Create the attachment in Rally
    cr = restApi.Create("Attachment", newAttachment);

    String attachRef = cr.Reference;
    Console.WriteLine("Created: " + attachRef);

}

public static byte[] ImageToByteArray(Image image, System.Drawing.Imaging.ImageFormat format)
{
    using (MemoryStream ms = new MemoryStream())
    {
        image.Save(ms, format);

        // Convert Image to byte[]                
        byte[] imageBytes = ms.ToArray();
        return imageBytes;
    }
}

解决方案

This issue also had me puzzled for a while - finally sorted it out about a week ago.

Two observations:

While Rally's SOAP API will serialize the byte array into a Base64 string behind the scenes, REST doesn't do this step for you and will expect a Base64-formatted String to be passed as the Content attribute for the AttachmentContent object. System.Drawing.Image.Length as shown in your example won't provide the correct length that Rally's WSAPI is expecting. You need to pass the length of the Base64-formatted string after being converted back to a regular String. This is also the same as the length of the byte array.

I'm including a code sample to illustrate:

// System Libraries
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
using System.Web;

// Rally REST API Libraries
using Rally.RestApi;
using Rally.RestApi.Response;

namespace RestExample_CreateAttachment
{
    class Program
    {
        static void Main(string[] args)
        {
            // Set user parameters
            String userName = "user@company.com";
            String userPassword = "password";

            // Set Rally parameters
            String rallyURL = "https://rally1.rallydev.com";
            String rallyWSAPIVersion = "1.36";

            //Initialize the REST API
            RallyRestApi restApi;
            restApi = new RallyRestApi(userName,
                                       userPassword,
                                       rallyURL,
                                       rallyWSAPIVersion);

            // Create Request for User
            Request userRequest = new Request("user");
            userRequest.Fetch = new List<string>()
                {
                    "UserName",
                    "Subscription",
                    "DisplayName",
                };

            // Add a Query to the Request
            userRequest.Query = new Query("UserName",Query.Operator.Equals,userName);

            // Query Rally
            QueryResult queryUserResults = restApi.Query(userRequest);

            // Grab resulting User object and Ref
            DynamicJsonObject myUser = new DynamicJsonObject();
            myUser = queryUserResults.Results.First();
            String myUserRef = myUser["_ref"];

            //Set our Workspace and Project scopings
            String workspaceRef = "/workspace/12345678910";
            String projectRef = "/project/12345678911";
            bool projectScopingUp = false;
            bool projectScopingDown = true;

            // Find User Story that we want to add attachment to

            // Tee up Story Request
            Request storyRequest = new Request("hierarchicalrequirement");
            storyRequest.Workspace = workspaceRef;
            storyRequest.Project = projectRef;
            storyRequest.ProjectScopeDown = projectScopingDown;
            storyRequest.ProjectScopeUp = projectScopingUp;

            // Fields to Fetch
            storyRequest.Fetch = new List<string>()
                {
                    "Name",
                    "FormattedID"
                };

            // Add a query
            storyRequest.Query = new Query("FormattedID", Query.Operator.Equals, "US43");

            // Query Rally for the Story
            QueryResult queryResult = restApi.Query(storyRequest);

            // Pull reference off of Story fetch
            var storyObject = queryResult.Results.First();
            String storyReference = storyObject["_ref"];

            // Read In Image Content
            String imageFilePath = "C:\\Users\\username\\";
            String imageFileName = "image1.png";
            String fullImageFile = imageFilePath + imageFileName;
            Image myImage = Image.FromFile(fullImageFile);

            // Get length from Image.Length attribute - don't use this in REST though
            // REST expects the length of the image in number of bytes as converted to a byte array
            var imageFileLength = new FileInfo(fullImageFile).Length;
            Console.WriteLine("Image File Length from System.Drawing.Image: " + imageFileLength);

            // Convert Image to Base64 format
            string imageBase64String = ImageToBase64(myImage, System.Drawing.Imaging.ImageFormat.Png);           

            // Length calculated from Base64String converted back
            var imageNumberBytes = Convert.FromBase64String(imageBase64String).Length;

            // This differs from just the Length of the Base 64 String!
            Console.WriteLine("Image File Length from Convert.FromBase64String: " + imageNumberBytes);

            // DynamicJSONObject for AttachmentContent
            DynamicJsonObject myAttachmentContent = new DynamicJsonObject();
            myAttachmentContent["Content"] = imageBase64String;

            try
            {
                CreateResult myAttachmentContentCreateResult = restApi.Create("AttachmentContent", myAttachmentContent);
                String myAttachmentContentRef = myAttachmentContentCreateResult.Reference;
                Console.WriteLine("Created: " + myAttachmentContentRef);

                // DynamicJSONObject for Attachment Container
                DynamicJsonObject myAttachment = new DynamicJsonObject();
                myAttachment["Artifact"] = storyReference;
                myAttachment["Content"] = myAttachmentContentRef;
                myAttachment["Name"] = "AttachmentFromREST.png";
                myAttachment["Description"] = "Attachment Desc";
                myAttachment["ContentType"] = "image/png";
                myAttachment["Size"] = imageNumberBytes;
                myAttachment["User"] = myUserRef;

                CreateResult myAttachmentCreateResult = restApi.Create("Attachment", myAttachment);

                List<string> createErrors = myAttachmentContentCreateResult.Errors;
                for (int i = 0; i < createErrors.Count; i++)
                {
                    Console.WriteLine(createErrors[i]);
                }

                String myAttachmentRef = myAttachmentCreateResult.Reference;
                Console.WriteLine("Created: " + myAttachmentRef);

            }
            catch (Exception e)
            {
                Console.WriteLine("Unhandled exception occurred: " + e.StackTrace);
                Console.WriteLine(e.Message);
            }
        }

        // Converts image to Base 64 Encoded string
        public static string ImageToBase64(Image image, System.Drawing.Imaging.ImageFormat format)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                image.Save(ms, format);
                // Convert Image to byte[]                
                byte[] imageBytes = ms.ToArray();

                // Convert byte[] to Base64 String
                string base64String = Convert.ToBase64String(imageBytes);

                return base64String;
            }
        }
    }
}