dapplo / Dapplo.Confluence

An Atlassian confluence client for .NET
MIT License
34 stars 20 forks source link

UpdateAsync throws NotSupportedException #46

Open cosmidumi opened 4 years ago

cosmidumi commented 4 years ago

Hello,

I have the following scenario:

`
previousPage.Body.Storage.Value = "some content"; previousPage.Version = new Dapplo.Confluence.Entities.Version { IsMinorEdit = false, Number = previousPage.Version.Number + 1 };

        try
        {
              await _confluenceClient.Content.UpdateAsync(previousPage);
        }
        catch(Exception ex)
        {
            throw ex;
        }

`

Every time the API call is made for updating a page I get the following exception: System.NotSupportedException: 'Unsupported result type Content & combination.'

Lakritzator commented 4 years ago

Hi

Cloud or server? And if server, what version?

cosmidumi commented 4 years ago

Hello, forgot to mention, Confluence Server, version 7.1.0

Lakritzator commented 4 years ago

Updating content is a bit of a special and complicated process, up to now I haven't done much to improve this. Currently my project just provides the available Atlassian API, without much additional support. What I mean is that you need quite a lot of knowledge to do so.

I tried this, and don't need any code changes in my project, but you really need to know what you are doing. I'm not a fan of this, so I would be more than happy to supply some additional help.

Currently you will need to call await GetAsync(contentId,new []{ "body.storage", "version" }) The content object returned will have body.storage and the version filled, which is important. From there on, your code should work. Let me know if this works!

Possible things I can do to improve the experience with my project:

Any suggestions?

cosmidumi commented 4 years ago

I have tried what you suggested, but the same error occurs. Here is what I tried:

` var previousPage = await _confluenceClient.Content.GetAsync(id, new[] { "body.storage", "version" });

        previousPage.Body.Storage.Value = "test";

        previousPage.Version.Number++;

        try
        {
            var content = await _confluenceClient.Content.UpdateAsync(previousPage);
            _logger.LogInformation($"Content {content.Id} was updated");
        }
        catch (Exception ex)
        {
            _logger.LogError($"An error has occurred when updating the page with id {previousPage.Id}: {ex.Message}");
            throw ex;
        }

`

Not sure if I missed anything; I have checked the API call that is made when you manually update a Confluence page and I can't see any difference (it seems that the "space" was also required when updating, but even if I include the "space" when retrieving the page, I get the same results, the same exception).

In my opinion, the best option for improvement would be to have some more documentation/examples for basic usage.

Lakritzator commented 4 years ago

That is weird, I've a test case: https://github.com/dapplo/Dapplo.Confluence/blob/master/src/Dapplo.Confluence.Tests/ContentTests.cs#L62 This was able to run on Cloud and Server (7.5) but we might be missing something here.

I'd love to see the complete logs, what logging implementation are you using?

This Dapplo.Confluence project is "quite" chatty, but you will need to forward the logging to your own logging solution, or add Dapplo.Loggers. I'm working to moving towards Microsoft.Extensions but this is currently only in my head.

Lakritzator commented 4 years ago

@cosmidumi Did you have a look at the test case I pointed you to?

cosmidumi commented 3 years ago

Sorry for the late reply. I have updated the package to the latest version, the issue still occurs.

Here is a sample of my code

using MyProject.Business.Helpers;
using MyProject.Domain.Contracts.Service;
using Dapplo.Confluence;
using Dapplo.Confluence.Entities;
using Dapplo.Confluence.Query;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace MyProject.Business.Services
{
    public class ConfluenceService : IConfluenceService
    {
        private readonly IConfluenceClient _confluenceClient;

        private readonly ILogger _logger;

        private const string FIELD_KEY_PREFIX = "Confluence:";

        public ConfluenceService(ILogger<ConfluenceService> logger, IConfiguration config)
        {
            _logger = logger;
            _confluenceClient = ConfluenceClient.Create(new Uri(config[$"{FIELD_KEY_PREFIX}ServerUrl"]));
            _confluenceClient.SetBasicAuthentication(EncryptionHelper.Decrypt(config[$"{FIELD_KEY_PREFIX}User"]), EncryptionHelper.Decrypt(config[$"{FIELD_KEY_PREFIX}Password"]));
        }

        public async Task UploadContentToPages(long[] ids, string htmlContent)
        {
            var existingPages = await GetPagesByIds(ids);
            ids.Where(id => !existingPages.Select(existingPage => existingPage.Id).Contains(id)).ToList()
                .ForEach(id => _logger.LogWarning($"Page with id {id} was not found in Confluence."));
            var tasks = existingPages.Select(page => UploadContentToPage(page, htmlContent));
            await Task.WhenAll(tasks);
        }

        private async Task UploadContentToPage(Content previousPage, string newContent)
        {
            previousPage.Body.Storage.Value = newContent;
            previousPage.Version = new Dapplo.Confluence.Entities.Version { IsMinorEdit = false, Number = previousPage.Version.Number + 1 };

            await _confluenceClient.Content.UpdateAsync(previousPage);
        }

        private async Task<IList<Content>> GetPagesByIds(long[] ids)
        {
            var query = Where.And(Where.Type.IsPage, Where.Id.In(ids));
            ConfluenceClientConfig.ExpandSearch = new[] { "body.storage", "version", "space", "ancestors" };

            var pages = await _confluenceClient.Content.SearchAsync(query, limit: 100);

            return pages.Results;
        }
    }
}

For the Update, I have ended up using RestSharp, building the request by hand as it follows. The only difference that I see, is the fact that I have to specify the representation as editor.

var request = new RestRequest("/rest/api/content/" + previousPage.Id + "?status=draft", Method.PUT);
request.RequestFormat = DataFormat.Json;
var body = new
{
    body = new
    {
        editor = new
        {
            content = new { Id = previousPage.Id },
            representation = "editor",
            value = newContent
        }
    },
    id = previousPage.Id,
    space = new { key = previousPage.Space.Key },
    status = "current",
    title = previousPage.Title,
    type = "page",
    version = new
    {
        minorEdit = true,
        number = previousPage.Version.Number + 1
    }
};
request.AddJsonBody(body);
_client.Put(request);