2

First I would like to describe our architecture, it consists of:

  • Outlook Web Addin (hosted on IIS on-prem)
  • an ASP.NET Web API (hosted on-prem)
  • the Microsoft Graph REST API in the cloud
  • a SOAP WebService (not under my control, on-prem).

The flow can be described with these steps:

  1. User clicks a button in the Outlook Web Add-in
  2. An Ajax request is made from the Web Add-in to the Web API with the ID of the message as a parameter.
  3. The Web API requests the message from Graph via GraphServiceClient
  4. The Web API packs the attachments from the message into a SOAP request and sends submits it to the SOAP service.

The attachments in Microsoft Graph are returned as a Byte Array and stored in memory. So we can get run into memory and performance issues.

My question is, would it be possible to stream the attachments from Microsoft Graph directly to the SOAP service?

Getting attachments from MS Graph Rest API

var mail = graphClient
    .Users["mailbox"]
    .Messages["id"]
    .Request()
    .Expand("attachments");

foreach (var attachment in message.Attachments)
{
    if (attachment.GetType() == typeof(FileAttachment))
    {
        var fileAttachment = (FileAttachment) attachment;

        // ==> fileAttachment.ContentBytes already contains the bytes of the attachment
    }
}

Should the bytes of the attachment be fetched with a second request? How can it be streamed down?

The definition of the attachment part in the soap service wsdl

<xsd:complexType name="DocumentData">
    <xsd:sequence>
        <xsd:element name="name" type="xsd:string"/>
        <xsd:element name="extension" type="xsd:string"/>
        <xsd:element name="content" type="xsd:base64Binary"/>
    </xsd:sequence>
</xsd:complexType>

... is generated in the service reference as

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.3062.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://is.uniqa.at/UniqaDocumentWF.WS:startDoWFProcess")]
public partial class DocumentData : object, System.ComponentModel.INotifyPropertyChanged
{

    private string nameField;
    private string extensionField;
    private byte[] contentField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 0)]
    public string name
    {
        get
        {
            return this.nameField;
        }
        set
        {
            this.nameField = value;
            this.RaisePropertyChanged("name");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 1)]
    public string extension
    {
        get
        {
            return this.extensionField;
        }
        set
        {
            this.extensionField = value;
            this.RaisePropertyChanged("extension");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, DataType = "base64Binary", Order = 2)]
    public byte[] content
    {
        get
        {
            return this.contentField;
        }
        set
        {
            this.contentField = value;
            this.RaisePropertyChanged("content");
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName)
    {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null))
        {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}

... and can be created like this:

DoWF.DocumentData data = new DocumentData();
data.name = name;
data.extension = extension;

// At the moment content is set to 
// "fileAttachment.ContentBytes" before 
// the request is send to the soap service
data.content = content; 

How and where can I stream the attachment bytes from Rest API over "my" Web API and then to the SOAP service?

I would also be grateful for a completely different approach to solve this problem.

Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
Mario Semper
  • 511
  • 1
  • 3
  • 17

1 Answers1

0

Given the number of layers involved and your inability to control the APIs on either end of the workflow, I'm not sure this approach would work. More importantly, I suspect even if you can get this working, you'll find it extremely fragile.

I would suggest something along the lines of this article from the documentation: Get attachments of an Outlook item from the server. This uses example uses Exchange Web Services rather than Microsoft Graph but the general flow would be the same:

  1. User clicks the button in the Add-in
  2. Add-in passes the userPrincipalName and the message id to Web API
  3. Web API downloads the attachments to temporary storage.
  4. Web API uploads the attachments from temporary storage to the SOAP service.

In order to make this work, your Web API will need to use Client Credentials to connect to Microsoft Graph using the Mail.Read application scope. This will allow the Web API to connect to any mailbox and download the attachments.

Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63