Technical dive for Implementing Blockchain for Shipment Management in SharePoint
Tags: sharepoint, blockchain, azure
Published: 2023-11-21 07:30:00

Embark on a journey where innovation meets real-world application! This post delves into the realm of integrating **blockchain** into **SharePoint**, demystifying its simplicity through a practical case. Picture a global company specializing in NBA shirt sales, our focal point today, grappling with the intricacies of shipments and deliveries. This scenario serves as a tangible example to unravel the role of blockchain in SharePoint amidst everyday business challenges.

> _This marks the second part, exploring the intricate technical aspects of the solution. Meanwhile, the initial segment, titled [Implementing Blockchain for Shipment and Delivery Management in SharePoint](https://intranetfromthetrenches.substack.com/p/implementing-blockchain-in-sharepoint), offers a comprehensive view of the business side and solution. We hope you find both segments enlightening!_

This exploration is not just for tech enthusiasts; it's an invitation to witness the magic of technological simplification. 

The article is divided into three parts, breaking down the data layer, the API layer, and the implementation of the business layer in **Azure Logic Apps**. Gain a comprehensive view with the accompanying technical diagram.

![Technical blueprint diagram of the solution](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23148db0-5923-45b8-80d4-986c3e4578ea_2718x1257.png)

## Data layer

The app data resides in **SharePoint Online**, requiring three lists: shipments, deliveries, and an event log capturing creation, modification, and deletion events for both. Essentially, this last list functions as our **blockchain storage**.

The `Shipment Information` list handles shipment management, featuring custom columns alongside standard **SharePoint Online** columns:
- `Shipment ID` records the shipment identifier.
- `Shipment Date` designates the shipment's scheduled date.
- `Shipment Information` captures additional details about the shipment.

![`Shipment Information` list](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8daea0d-a30c-4503-9531-8b38b020eca6_1136x361.png)

The `Delivery Details` list manages delivery content and includes custom columns alongside standard **SharePoint Online** columns:
- `Delivery ID` records the delivery identifier.
- `Delivery Date` designates the scheduled delivery date.
- `Shipment ID` records the associated shipment identifier, linking to the corresponding shipment record.
- `Recipient Information` captures additional details about the delivery.

![`Delivery Details` list](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feed7f069-54d1-450b-b53e-614c89a32e1f_1252x242.png)

Finally, the list that will be the store of our blockchain is called `Event Tracking Information` and, apart from the standard **SharePoint** columns, it has the following custom columns distributed in 2 types of content:

- For the `Shipment Block Event` content type:
	- `Shipment ID`,
	- `Shipment Date`,
	- `Shipment Author`,
	- `Record Creation`,
	- `Block Hash`
- For the `Delivery Block Event` content type:
	- `Delivery ID`,
	- `Delivery Date`,
	- `Shipment ID`,
	- `Delivery Author`,
	- `Record Creation`,
	- `Block Hash`

Finally, the list designated as our blockchain repository is named `Event Tracking Information`. In addition to standard **SharePoint** columns, it includes the following custom columns distributed across two content types.

For the `Shipment Block Event` content type: `Shipment ID`, `Shipment Date`, `Shipment Author`, `Record Creation`, and `Block Hash`.

For the `Delivery Block Event` content type: `Delivery ID`, `Delivery Date`, `Shipment ID`, `Delivery Author`, `Record Creation`, and `Block Hash`.

![`Event Tracking Information` list settings](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0d1bae1-a790-45c8-9398-0c6a4210683e_1551x1142.png)


## API Layer

The API layer implementation in this exercise is straightforward. It consists of a single function developed in *Azure Functions* using *Node.js* as the engine for executing functionality.

Below, you'll find the source code for the function, allowing you to observe its simplicity despite being packed with validations and log messages.

```JavaScript
const crypto = require("crypto");
const { app } = require('@azure/functions');

const secret = 'YOUR SECRET HERE';


function getSourceParameterNotFoundBodyAnswer() {
    return {
        jsonBody: {
            message: 'source parameter must be provided'
        },
        status: 400
    };
}


app.http('generateHashFromText', {
    methods: ['post'],
    route: 'hash/text',
    authLevel: 'anonymous',
    handler: async (request, context) => {
        try {
            context.log(`Http function processed request for url "${request.url}"`);
            
            context.trace(`Obtaining body parameters...`);
            
            // Checking if there is something in the body content...
            if(request.body === undefined || request.body === null) {
                context.warn(`No body parameters have been provided.`);
                return getSourceParameterNotFoundBodyAnswer();
            }

            // Checking if there is something in the body out of being empty...
            const bodyContent = await request.body.getReader().read();
            if(bodyContent.value.length === 0) {
                context.warn(`No body parameters have been provided.`);
                return getSourceParameterNotFoundBodyAnswer();
            }

            // Reading source parameter from the body
            const body = JSON.parse(new TextDecoder().decode(bodyContent.value));
            const source = body.source;

            if(source === undefined || source === null || source === '') {
                context.info(`source query parameter not found.`);
                return getSourceParameterNotFoundBodyAnswer();
            }

            context.info(`source query parameter value: ${source}`);

            // Doing the hash to the source parameter content...
            context.trace(`Do the hash for source value...`);
            const hasher = crypto.createHmac("sha256", secret);
            const result = hasher.update(source).digest('hex');

            context.trace(`Hash obtained is ${result}`);

            // Returning the hash....
            return {
                jsonBody: {
                    hash: result,
                    source: source,
                },
                status: 200
            };
        }
        catch(err) {
            context.error(`An unknow error has ocurred: ${err}`);
            return {
                jsonBody: {
                    err: err
                },
                status: 500
            };
        }
	}
});
```



## Azure Logic Apps and Custom Connector

### Custom connector configuration

The setup of the **Custom Connector** is straightforward. After implementing the API, adding the relevant information is all that's required. If you're acquainted with *OpenAPI* or *Swagger*, this process is intuitive. For those unfamiliar, Microsoft's guide, [Create a custom connector from scratch](https://learn.microsoft.com/en-us/connectors/custom-connectors/define-blank), provides step-by-step instructions. Pay attention to the settings in the third step, which become visible when you select it as an action during the deployment of **Azure Logic Apps**.

![Step 3 of the Custom Connector configuration](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F373b6c8b-a507-4cea-9fdd-17247f5c403e_1726x1179.png)

### When a new Shipment is created in Azure Logic Apps

I'd like to walk you through the implementation details of a specific **Azure Logic App**—specifically, the one triggered after a new item is submitted. The rest of the **Azure Logic Apps** work in a similar way but with the corresponding trigger for modification and deletion.

The implementation unfolds in six phases, each with a distinct and detailed objective.

![Azure Logic Apps On Shipment Creation Event](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09f4d24b-65d0-49c4-97a1-2eada7141161_928x855.png)


#### 1. The trigger
The trigger is set up to identify any newly created item in the Shipment list within **SharePoint Online**. It's a straightforward process where we simply specify the **SharePoint Online** site and choose the relevant list.

![Phase 1 - SharePoint Online Trigger](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e30f078-2f33-4c8f-ad12-5dd76b041956_628x302.png)


#### 2. Variable initialization
I want to underscore the significance of establishing these two stream variables. Initially, to preserve the hash of our **blockchain's** latest block, we initialize a variable named `LatestBlockHash`. Given that hashes are alphanumeric, we set its type to `string`. The default value is set to 0, serving as the hash for the genesis block or the chain's first block in case no blocks exist yet.

Conversely, we also initialize the `ShipmentBlockHash` variable, also of type `string`, to store the hash of the upcoming block. This variable guides us throughout the process and provides a traceable record of the value generated at each stage if needed.

![Phase 2 - Variable set up](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bd4418e-7d30-43c9-9052-6cbfde323bcf_634x390.png)


#### 3. Get the latest block hash
At this stage, we navigate to our repository housing the **blockchain** to fetch the latest block and consequently, obtain the value of the property where its hash is stored. Our **blockchain** resides in a **SharePoint Online** list, and that's where we retrieve the latest block.

As illustrated in the image, we utilize the action to fetch items from the **SharePoint Online** list. We specify the site and list, and for enhanced performance, we arrange the items based on the creation date column, ordered from newest to oldest. Additionally, we opt to retrieve only one item, streamlining the process for the system to efficiently return the desired item.

Following this, we assign the value of the `Block Hash` column to the `LatestBlockHash` variable, initialized earlier, enabling its incorporation into the new block.

![Phase 3 - Obtaining the latest block hash](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5419544-e603-4052-bdad-d56656515790_559x680.png)

#### 4. Generate the shipment block
This step is straightforward; we only need to provide the necessary information to our **Custom Connector**. As mentioned earlier, **Azure Logic Apps** lacks an action to generate a hash value. Hence, we've created an API to fulfill this need and configured an **Azure Logic Apps Custom Connector** for its utilization.

The text string we transmit to generate a new block comprises the properties of the created Shipment and the hash value of the last block in the **blockchain**. The configuration of the `source` parameter in the **Custom Connector** is outlined below:

```
concat('Shipment#;#',triggerBody()?['Shipment_x0020_ID'],'#;#',triggerBody()?['Shipment_x0020_Date'],'#;#',triggerBody()?['Author']?['Email'],'#;#',triggerBody()?['Created'],'#;#',variables('LatestBlockHash'))
```

> *Commencing with the text string 'Shipment' for block type identification, the following parameters consist of the ID field, shipment date, SharePoint item creator, item creation date, and ultimately, the hash of the last blockchain block. The string '#;#' functions as a separator for these values.*

Following that, we assign the generated hash value to the `ShipmentBlockHash` variable.

![Phase 4 - Generation of the new hash](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f043a3-7486-4b1a-bfe1-8215efe465a8_1192x591.png)

#### 5. Add block event to the blockchain list
By utilizing the function to append a new item to a **SharePoint Online** list, we store our recently created block, capturing the transaction, such as the initiation of a new shipment in this instance.

The setup is straightforward; we commence by designating the **SharePoint Online** site and the list associated with the event log. Subsequently, we input the appropriate values into the corresponding fields. Notably, the `Block Hash` field is of particular importance, as it receives the freshly generated hash from the preceding stage, stored in the `ShipmentBlockHash` variable.

![Phase 5 - Adding the new block to the blockchain](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e721a6a-7bd3-4e2e-ad59-34cf312cfde1_769x610.png)

#### 6. Notification
The final stage is discretionary. In my scenario, I opted to dispatch an email to the administrator whenever a block is generated, serving as a notification for the completed activity. Alternatively, this step can be substituted with any other form of notification or annotation within another system, or it can be omitted from the workflow entirely.

![Phase 6 - Notification](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66e7e33a-4f28-4ce8-9dd0-f1e541ee50a3_759x528.png)

## Conclusion

I trust that engaging in this exercise has sparked inspiration for various ideas as you reflect on its content. The underlying objective is to explore diverse technologies, understand their potential, and gain hands-on experience. Through this process, my aim is to share these insights with you, not only as a record of experimentation but also as a source of encouragement for your own ventures and explorations in the realm of technology and innovation.

By delving into this exercise, we open doors to new possibilities and discoveries. It's a testament to the spirit of experimentation, demonstrating the potential of blending different technologies. As we navigate through these technical intricacies, the ultimate goal is not only to showcase what's achievable but also to inspire a community of enthusiasts, encouraging them to embark on their unique journeys of exploration and technological experimentation.


## References

- *Implementing Blockchain for Shipment and Delivery Management in SharePoint: [https://intranetfromthetrenches.substack.com/p/implementing-blockchain-in-sharepoint](https://intranetfromthetrenches.substack.com/p/implementing-blockchain-in-sharepoint)*
- *Custom connectors: [https://learn.microsoft.com/en-us/connectors/custom-connectors/](https://learn.microsoft.com/en-us/connectors/custom-connectors/)*
- *Create a custom connector from scratch: [https://learn.microsoft.com/en-us/connectors/custom-connectors/define-blank](https://learn.microsoft.com/en-us/connectors/custom-connectors/define-blank)*
- *Write code in a custom connector: [https://learn.microsoft.com/en-us/connectors/custom-connectors/write-code](https://learn.microsoft.com/en-us/connectors/custom-connectors/write-code)*
- _Thanks to [Hector Sosa](https://dev.to/ekqt), the author of usescreenshot.app application, for allowing framing pictures: [https://usescreenshot.app/](https://usescreenshot.app/)_