DelphiUser
DelphiUser

Reputation: 419

VersionOne API for embedded images returns 404 (not found)

I'm trying to write a tool (in C#) to read the embedded images from a VersionOne asset (story/defect) and store them locally (to be later able to migrate them to another place).

I found this link for reference: https://community.versionone.com/Digital.ai_Agility_Integrations/Developer_Library/Get_an_SDK/.NET_SDK/Working_with_Attachments_and_Images

With that I was able to read attachments successfully, but embedded images are still a problem.

My current attempt for this is like this:

        public string StoreActiveV1ItemEmbeddedImages(PrimaryWorkitem v1Item, string targetPath, out int countStored)
    {
        string result = string.Empty;
        countStored = 0;
        if (v1Item == null) return "Error: No V1 item given to StoreActiveV1ItemEmbeddedImages!";
        string filePath = targetPath;
        if (!filePath.EndsWith("\\")) filePath += "\\";
        if (!Directory.Exists(filePath)) return "Error: Given path '" + filePath + "' does not exist!";
        IServices services = v1Instance.ApiClient.Services;
        IMetaModel metaModel = v1Instance.ApiClient.MetaModel;
        IAttachments attachments = v1Instance.ApiClient.Attachments;
        Oid itemOid = services.GetOid(v1Item.ID.Token); 
        IAssetType assetType = metaModel.GetAssetType("EmbeddedImage");
        var query = new Query(assetType);
        IAttributeDefinition contentAttribute = assetType.GetAttributeDefinition("Content");
        IAttributeDefinition contentTypeAttribute = assetType.GetAttributeDefinition("ContentType");
        IAttributeDefinition assetAttribute = assetType.GetAttributeDefinition("Asset");
        query.Selection.Add(contentAttribute);
        query.Selection.Add(contentTypeAttribute);
        query.Selection.Add(assetAttribute);
        FilterTerm term = new FilterTerm(assetAttribute);
        term.Equal(itemOid.Momentless);
        query.Filter = term;
        QueryResult queryResult = services.Retrieve(query);
        foreach (Asset attachment in queryResult.Assets)
        {
            VersionOne.SDK.APIClient.Attribute attribute = attachment.GetAttribute(contentTypeAttribute);
            string mimeType = "image/png";
            if (attribute != null)
            {
                mimeType = attribute.Value.ToString();
            }
            string extension = MimeTypeStringToExtensionForImages(mimeType);
            string fileName = (countStored + 1) + "_" + extension;
            string attachmentID = attachment.Oid.Key.ToString();
            string content = attachment.GetAttribute(contentAttribute).Value.ToString();
            using (var fileStream = File.Create(filePath + "v1_img_" + fileName))
            {
                using (Stream blob = attachments.GetReadStream(content))
                {
                    blob.CopyTo(fileStream);
                }

                countStored++;
            }
        }

        return result;
    }

I get a problem in this line

using (Stream blob = attachments.GetReadStream(content))

The server responds with a 404 like this: "The remote server returned an error: (404) Not Found.'"

Below can be seen the values in the debugger: Debugged values

I've tried this with both the content (/VersionOne/embedded.img/2041732) and the attachmentID (2041732), but both result in the same error.

What should I use in this to find the data, or have I understood this more severely wrong and need to have a different approach?

Any help would be greatly appreciated!

Upvotes: 0

Views: 111

Answers (1)

DelphiUser
DelphiUser

Reputation: 419

I think I found an answer. Not sure if it's the best or even a good answer, but at least an answer that for now seems to be enough for me.

The "content" is a path which can be appended to the VersionOne base URL and together they point to a location from which the embedded image data can be downloaded.

So my modified code now looks like this:

        public string StoreActiveV1ItemEmbeddedImages(PrimaryWorkitem v1Item, string targetPath, out int countStored)
    {
        string result = string.Empty;
        countStored = 0;
        if (v1Item == null) return "Error: No V1 item given to StoreActiveV1ItemEmbeddedImages!";
        string filePath = targetPath;
        if (!filePath.EndsWith("\\")) filePath += "\\";
        if (!Directory.Exists(filePath)) return "Error: Given path '" + filePath + "' does not exist!";
        IServices services = v1Instance.ApiClient.Services;
        IMetaModel metaModel = v1Instance.ApiClient.MetaModel;
        //IAttachments attachments = v1Instance.ApiClient.Attachments;
        Oid itemOid = services.GetOid(v1Item.ID.Token); 
        IAssetType assetType = metaModel.GetAssetType("EmbeddedImage");
        var query = new Query(assetType);
        IAttributeDefinition contentAttribute = assetType.GetAttributeDefinition("Content");
        IAttributeDefinition contentTypeAttribute = assetType.GetAttributeDefinition("ContentType");
        IAttributeDefinition assetAttribute = assetType.GetAttributeDefinition("Asset");
        query.Selection.Add(contentAttribute);
        query.Selection.Add(contentTypeAttribute);
        query.Selection.Add(assetAttribute);
        FilterTerm term = new FilterTerm(assetAttribute);
        term.Equal(itemOid.Momentless);
        query.Filter = term;
        QueryResult queryResult = services.Retrieve(query);
        foreach (Asset attachment in queryResult.Assets)
        {
            VersionOne.SDK.APIClient.Attribute attribute = attachment.GetAttribute(contentTypeAttribute);
            string mimeType = "image/png";
            if (attribute != null)
            {
                mimeType = attribute.Value.ToString();
            }
            string extension = MimeTypeStringToExtensionForImages(mimeType);
            string attachmentID = attachment.Oid.Key.ToString();
            string fileName = attachmentID + "_" + extension;
            string content = attachment.GetAttribute(contentAttribute).Value.ToString();
            string urlPath = Config.url;
            const string commonBit = "/VersionOne";
            if (urlPath.EndsWith(commonBit) && content.StartsWith(commonBit))
            {
                content = content.Replace(commonBit, string.Empty);
            }
            content = urlPath + content;
            string finalFileName = filePath + "v1_img_" + fileName;
            using (var client = new System.Net.WebClient())
            {
                client.Credentials = new System.Net.NetworkCredential(config.login, config.password);
                client.DownloadFile(content, finalFileName);
                countStored++;
            }
        }

        return result;
    }

So the URL is made from configuration data and the content string (just a bit of handling to ensure the "VersionOne" is only once in the final string) and then downloaded from there using WebClient instead of VersionOne API.

Upvotes: 0

Related Questions