(enebular問い合わせ結果の共有)functionノードで外部npmモジュールを使用すると、クラウド実行環境でエラーが発生する

# はじめに

functionノードの外部npmモジュールを使用する場合、enebularのNode-REDフローエディタは正常に動作しますが、クラウド実行環境ではエラーが発生します。
enebularでクラウド実行環境を利用する場合、外部npmモジュールの使用に注意！
今後の機能拡張に期待します。

### 2024年2月3日 追記

外部npmモジュールを使用せず、functionノードにコード(npmモジュールの関数)を書くことでエラーを回避しました。

```javascript
function getBoundary(header){
	if (typeof header === 'undefined' || header === null) {
        return "";
    }
	var items = header.split(';');
	if(items)
		for(let i=0;i<items.length;i++){
			var item = (new String(items[i])).trim();
			if(item.indexOf('boundary') >= 0){
				var k = item.split('=');
				return (new String(k[1])).trim();
			}
		}
	return "";
}

function obj(str){
	if (typeof str === 'undefined' || str === null) {
        return {};
    }
	var k = str.split('=');
	if (k.length < 2) { // '='で分割した結果が2つの要素を持たない場合
        return {}; // または適切なエラーハンドリング
    }
	var a = k[0].trim();
	var b = JSON.parse(k[1].trim());
	var o = {};
	Object.defineProperty( o , a , 
		{ value: b, writable: true, enumerable: true, configurable: true })
	return o;
}

function process1(part){
	if (typeof part.header === 'undefined' || part.header === null) {
       	return {};
    }
	var header = part.header.split(';');
	var file = obj(header.length > 2 ? header[2] : null); // header[2]が存在しない場合はnullを渡す
    if (typeof part.info === 'undefined' || part.info === null || !part.info.includes(':')) {
       	return file; // part.infoが不正な形式の場合はfileをそのまま返す
    }
    var contentTypeParts = part.info.split(':');
    if (contentTypeParts.length < 2) {
       	return file; // ':'で分割した結果が2つの要素を持たない場合
    }		
	var file = obj(header[2]);
	var contentType = part.info.split(':')[1].trim();		
	Object.defineProperty( file , 'type' , 
		{ value: contentType, writable: true, enumerable: true, configurable: true })
	Object.defineProperty( file , 'data' , 
		{ value: new Buffer(part.part), writable: true, enumerable: true, configurable: true })
	return file;
}

function Parse(multipartBodyBuffer,boundary){
	var prev = null;
	var lastline='';
	var header = '';
	var info = ''; var state=0; var buffer=[];
	var allParts = [];

	for(let i=0;i<multipartBodyBuffer.length;i++){
		var oneByte = multipartBodyBuffer[i];
		var prevByte = i > 0 ? multipartBodyBuffer[i-1] : null;
		var newLineDetected = ((oneByte == 0x0a) && (prevByte == 0x0d)) ? true : false;
		var newLineChar = ((oneByte == 0x0a) || (oneByte == 0x0d)) ? true : false;

		if(!newLineChar)
			lastline += String.fromCharCode(oneByte);

		if((0 == state) && newLineDetected){
			if(("--"+boundary) == lastline){
				state=1;
			}
			lastline='';
		}else
		if((1 == state) && newLineDetected){
			header = lastline;
			state=2;
			lastline='';
		}else
		if((2 == state) && newLineDetected){
			info = lastline;
			state=3;
			lastline='';
		}else
		if((3 == state) && newLineDetected){
			state=4;
			buffer=[];
			lastline='';
		}else
		if(4 == state){
			if(lastline.length > (boundary.length+4)) lastline=''; // mem save
			if(((("--"+boundary) == lastline))){
				var j = buffer.length - lastline.length;
				var part = buffer.slice(0,j-1);
				var p = { header : header , info : info , part : part  };
				allParts.push(process1(p));
				buffer = []; lastline=''; state=5; header=''; info='';
			}else{
				buffer.push(oneByte);
			}
			if(newLineDetected) lastline='';
		}else
		if(5==state){
			if(newLineDetected)
				state=1;
		}
	}
	return allParts;
};

const body = msg.payload;
const lines = body.toString().split('\r\n');
const boundary = lines[0].slice(2);

const parts = Parse(body, boundary);

//msg.payload = parts;
msg.payload = parts[0].data;

return msg;
```

### カスタムノード

ついでにNode-REDのカスタムノードとenebularのプライベートノードを作成しました。

- enebularのプライベートノード

https://github.com/kitazaki/enebular-privatenode-contrib-parse-multipart-data01

- Node-REDのカスタムノード

https://github.com/kitazaki/node-red-contrib-parse-multipart-data01

(参考)

https://qiita.com/kazuhitoyokoi/items/0b8d7efeb27ee617ad12

https://qiita.com/kazuhitoyokoi/items/2e34ad85caace099745f


# 問い合わせ内容

![スクリーンショット 2024-02-01 22.46.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/5c085dbf-a987-9a5a-d6b4-5e6d4cdf4efc.png)

# 回答結果

![スクリーンショット 2024-02-01 22.46.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/2bc1647a-1a5d-67da-44ab-c28b0095c8b3.png)

# Node-REDフロー

- s3-test-01プロジェクト

  - lcdp-image-upload-test-01フロー

## フローエディタ

![スクリーンショット 2024-02-03 10.11.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/9b49ae76-ebdd-1805-7d06-2f382cac4c4f.png)


## データストア

![スクリーンショット 2024-02-03 10.11.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/2a5ca310-7850-b8c1-c9e3-611ba02241f2.png)


## multipart/form-dataデータを入力し、構造化データを出力するfunctionノード

![スクリーンショット 2024-01-29 15.19.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/0358bc3b-6cdf-e263-e96b-2d44329c0548.png)

![スクリーンショット 2024-01-29 15.20.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/8208f2c7-65de-8372-bf09-c337409edf2e.png)


# クラウド実行環境のログ

```
Welcome to Node-RED
===================

[Jan 28th 2024, 23:04:55]:  2024-01-28T14:04:55.018Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:04:55 - [info] Node-RED version: v3.0.2-c.2

[Jan 28th 2024, 23:04:55]:  2024-01-28T14:04:55.018Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:04:55 - [info] Node.js  version: v18.18.2

[Jan 28th 2024, 23:04:55]:  2024-01-28T14:04:55.018Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:04:55 - [info] Linux 5.10.201-213.748.amzn2.x86_64 x64 LE

[Jan 28th 2024, 23:04:58]:  2024-01-28T14:04:58.618Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:04:58 - [info] Loading palette nodes

[Jan 28th 2024, 23:05:06]:  2024-01-28T14:05:06.319Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:06 - [info] Context store  : 'default' [module=memory]

[Jan 28th 2024, 23:05:06]:  2024-01-28T14:05:06.336Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	Node-RED server started.

[Jan 28th 2024, 23:05:06]:  2024-01-28T14:05:06.397Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:06 - [warn] Using unencrypted credentials

[Jan 28th 2024, 23:05:06]:  2024-01-28T14:05:06.437Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:06 - [info] Installing module: parse-multipart-data, version: latest

[Jan 28th 2024, 23:05:07]:  2024-01-28T14:05:07.337Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	passing event to aws-lambda-io event emitter

[Jan 28th 2024, 23:05:15]:  2024-01-28T14:05:15.039Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:15 - [error] Installation of module parse-multipart-data failed:

[Jan 28th 2024, 23:05:15]:  2024-01-28T14:05:15.039Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:15 - [error] ------------------------------------------

[Jan 28th 2024, 23:05:15]:  2024-01-28T14:05:15.039Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:15 - [error] npm WARN config production Use `--omit=dev` instead.
npm ERR! code ENOENT
npm ERR! syscall mkdir
npm ERR! path /home/sbx_user1051
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, mkdir '/home/sbx_user1051'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

npm ERR! Log files were not written due to an error writing to the directory: /home/sbx_user1051/.npm/_logs
npm ERR! You can rerun the command with `--loglevel=verbose` to see the logs in your terminal

[Jan 28th 2024, 23:05:15]:  2024-01-28T14:05:15.056Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:15 - [error] ------------------------------------------

[Jan 28th 2024, 23:05:15]:  2024-01-28T14:05:15.076Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:15 - [info] Failed to load external modules required by this flow:

[Jan 28th 2024, 23:05:15]:  2024-01-28T14:05:15.076Z	9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	INFO	28 Jan 14:05:15 - [info]  - parse-multipart-data [unexpected_error]

[Jan 28th 2024, 23:05:15]:  END RequestId: 9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0

[Jan 28th 2024, 23:05:15]:  REPORT RequestId: 9f7d3cb9-ffc6-4f2e-9195-e8cf8030afa0	Duration: 20230.99 ms	Billed Duration: 20231 ms	Memory Size: 256 MB	Max Memory Used: 209 MB	Init Duration: 725.75 ms	
```