Teruhiro Komaki

日々の生活や、プログラミング、Claris FileMakerに関する情報をメモしておく雑記帳です。

FileMakerでURLをパースするカスタム関数を作りました

2023-01-06

AWSのSDKなどを使わずに、AWSのAPIを利用する際に、リクエストのヘッダーに署名を含める必要があります。

詳細は、以下のリンクを参照して頂きたいです。

AWS API リクエストの署名 - AWS 全般のリファレンス

AWS API リクエストの署名 - AWS 全般のリファレンス

海外のサンプルファイルを色々と見ましたが、やはり、自分で手を動かして、コードを書かないと理解できないので、ドキュメントを見て実装しました。

手が空いたら、会社のブログで、AWSのAPIを実行する方法については、記事を書きたいと思います…

...

更新履歴(2022–12–27)

会社のブログを更新しました。

FileMaker から S3 や EC2 などの AWS の API を利用する方法 | フルーデンス

FileMaker から S3 や EC2 などの AWS の API を利用する方法 | フルーデンス

...

更新履歴(2023–03–06)

カスタム関数にバグがあったので、修正しました。

...

上記のAWSのドキュメントの通りですが、AWSのAPI をリクエストする際に、hostとかパスとかクエリーが必要になります。

そのためにタイトルの通り、カスタム関数を作りました。

余談ですが…

カスタム関数を公開しましたら Nice!! とコメントを頂きました。

一言だとしても、コメント頂くと意外に嬉しいものですね。

私も、今後は Nice!! など気軽にコメントするように心がけます。

カスタム関数について

ParseUrl ( _url )

試したい方は、以下のURLを参照して、自分のカスタムAppに追加して頂ければと思います。

FileMaker Custom Function: ParseUrl ( _url )

FileMaker Custom Function: ParseUrl ( _url )

コード

/* ---------------------------------------------------------------------------
ParseUrl ( _url )

Description:
Parse the URL and return the result as JSON.

Note:
- If urlIsEmpty is 'false', the URL may have failed to parse.
- Please let me know if there is anything I have overlooked.
- For the structure of the URL, I referred to the URL below.
  https://en.wikipedia.org/wiki/URL

Example:
ParseUrl ( "https://username:[email protected]:80/dir1/file.txt?q1=v1&q2=v2#fragment" )
-> {"fileName":"file.txt","fragment":"fragment","host":"bucketname.s3.ap-northeast-1.amazonaws.com","password":"password","path":"/dir1/file.txt","port":"80","query":"q1=v1&q2=v2","scheme":"https","urlIsEmpty":true,"userName":"username"}

ParseUrl ( "https://en.wikipedia.org/wiki/URL" )
-> {"fileName":"","fragment":"","host":"en.wikipedia.org","password":"","path":"/wiki/URL","port":"","query":"","scheme":"https","urlIsEmpty":true,"userName":""}

History:
2022-11-24, Teruhiro Komaki <[email protected]>
--------------------------------------------------------------------------- */

Let ( [
// _url = "https://username:[email protected]:80/dir1/file.txt?q1=v1&q2=v2#fragment" ;
~url = _url ;

~scheme = GetValue ( Substitute ( ~url ; "://" ; "¶" ) ; 1 ) ;
~url = Substitute ( ~url ; ~scheme & "://" ; "" ) ;

~userinfo = If ( PatternCount ( ~url ; "@" ) ; GetValue ( Substitute ( ~url ; "@" ; "¶" ) ; 1 ) ) ;
~userinfoList = Substitute ( ~userinfo ; ":" ; "¶" ) ;
~userName = GetValue ( ~userinfoList ; 1 ) ;
~password = GetValue ( ~userinfoList ; 2 ) ;
~url = If ( not IsEmpty ( ~userinfo ) ; Substitute ( ~url ; ~userinfo & "@" ; "" ) ; ~url ) ;

~host = GetValue ( Substitute ( ~url ; [ ":" ; "¶" ] ; [ "/" ; "¶" ] ; [ "?" ; "¶" ] ; [ "#" ; "¶" ] ) ; 1 ) ;
~url = Substitute ( ~url ; ~host ; "" ) ;

~port = GetAsNumber ( GetValue ( Substitute ( ~url ; [ "/" ; "¶" ] ; [ "?" ; "¶" ] ; [ "#" ; "¶" ] ) ; 1 ) ) ;
~url = If ( not IsEmpty ( ~port ) ; Substitute ( ~url ; ":" & ~port ; "" ) ; ~url ) ;

~path = GetValue ( Substitute ( ~url ; [ "?" ; "¶" ] ; [ "#" ; "¶" ] ) ; 1 ) ;
~url = If ( not IsEmpty ( ~path ) ; Substitute ( ~url ; ~path ; "" ) ; ~url ) ;

~pathList = Substitute ( ~path ; "/" ; "¶" ) ;
~pathListLast = GetValue ( ~pathList ; ValueCount ( ~pathList ) ) ;
~fileName = If ( GetAsBoolean ( PatternCount ( ~pathListLast ; "." ) ) ; ~pathListLast ; "" ) ;

~fragment = GetValue ( Substitute ( ~url ; [ "#" ; "¶" ] ) ; 2 ) ;
~url = If ( not IsEmpty ( ~fragment ) ; Substitute ( ~url ; "#" & ~fragment ; "" ) ; ~url ) ;

~query = GetValue ( Substitute ( ~url ; [ "?" ; "¶" ] ) ; 2 ) ;
~url = If ( not IsEmpty ( ~query ) ; Substitute ( ~url ; "?" & ~query ; "" ) ; ~url ) ;

~urlIsEmpty = If ( IsEmpty ( ~url ) ; True ; False )

] ;

JSONSetElement ( "" ;
[ "scheme" ; ~scheme ; JSONString ] ;
[ "userName" ; ~userName ; JSONString ] ;
[ "password" ; ~password ; JSONString ] ;
[ "host" ; ~host ; JSONString ] ;
[ "port" ; ~port ; JSONString ] ;
[ "path" ; ~path ; JSONString ] ;
[ "fileName" ; ~fileName ; JSONString ] ;
[ "fragment" ; ~fragment ; JSONString ] ;
[ "query" ; ~query ; JSONString ] ;
[ "urlIsEmpty" ; ~urlIsEmpty ; JSONBoolean ]
)

) /*Let*/

コード(2023-03-06)

/* ---------------------------------------------------------------------------
ParseUrl ( _url )

Description:
Parse the URL and return the result as JSON.

Note:
- If urlIsEmpty is 'false', the URL may have failed to parse.
- Please let me know if there is anything I have overlooked.
- For the structure of the URL, I referred to the URL below.
   https://en.wikipedia.org/wiki/URL

Example:
ParseUrl ( "https://username:[email protected]:80/dir1/file.txt?q1=v1&q2=v2#fragment" )
-> {"fileName":"file.txt","fragment":"fragment","host":"bucketname.s3.ap-northeast-1.amazonaws.com","password":"password","path":"/dir1/file.txt","port":"80","query":"q1=v1&q2=v2","scheme":"https","urlIsEmpty":true,"userName":"username"}

ParseUrl ( "https://en.wikipedia.org/wiki/URL" )
-> {"fileName":"","fragment":"","host":"en.wikipedia.org","password":"","path":"/wiki/URL","port":"","query":"","scheme":"https","urlIsEmpty":true,"userName":""}

ParseUrl ( "https://username:[email protected]:80/path/to/file.txt?list-type=2&prefix=s3Prefix/a/b/#fragment" )
-> {"fileName":"file.txt","fragment":"fragment","host":"bucket-demo.s3.us-east-1.amazonaws.com","password":"password","path":"/path/to/file.txt","port":"80","query":"list-type=2&prefix=s3Prefix/a/b/","scheme":"https","urlIsEmpty":true,"userName":"username"}

History:
2022-11-24, Teruhiro Komaki 
create.
2023-02-24, Teruhiro Komaki 
It was fixed because it did not consider the case where "/" was included in the query.
--------------------------------------------------------------------------- */

Let ( [
  // _url = "https://username:[email protected]:80/path/to/file.txt?list-type=2&prefix=s3Prefix/a/b/#fragment" ;
  ~url = _url ;
  ~urlLength = Length ( ~url ) ;

  // scheme
  ~scheme = GetValue ( Substitute ( ~url ; "://" ; "¶" ) ; 1 ) ;
  ~schemeLength = Length ( ~scheme & "://" ) ;
  ~url = Middle ( ~url ; ( ~schemeLength + 1 ) ; ~urlLength ) ;

  //userInfo
  ~userinfo = If ( PatternCount ( ~url ; "@" ) ; GetValue ( Substitute ( ~url ; "@" ; "¶" ) ; 1 ) ) ; // username:password@
  ~userinfoLength = Length ( ~userinfo & "@" ) ;
  ~userinfoList = Substitute ( ~userinfo ; ":" ; "¶" ) ;
  ~userName = GetValue ( ~userinfoList ; 1 ) ;
  ~password = GetValue ( ~userinfoList ; 2 ) ;
  ~url = If ( not IsEmpty ( ~userinfo ) ; Middle ( ~url ; ( ~userinfoLength + 1 ) ; ~urlLength ) ; ~url ) ;

  // host
  ~host = GetValue ( Substitute ( ~url ; [ ":" ; "¶" ] ; [ "/" ; "¶" ] ; [ "?" ; "¶" ] ; [ "#" ; "¶" ] ) ; 1 ) ;
  ~hostLength = Length ( ~host ) ;
  ~url = Middle ( ~url ; ( ~hostLength + 1 ) ; ~urlLength ) ;

  // port
  ~port = GetAsNumber ( GetValue ( Substitute ( ~url ; [ "/" ; "¶" ] ; [ "?" ; "¶" ] ; [ "#" ; "¶" ] ) ; 1 ) ) ;
  ~portLength = Length ( ":" & ~port ) ;
  ~url = If ( not IsEmpty ( ~port ) ; Middle ( ~url ; ( ~portLength + 1 ) ; ~urlLength ) ; ~url ) ;

  // path
  ~path = GetValue ( Substitute ( ~url ; [ "?" ; "¶" ] ; [ "#" ; "¶" ] ) ; 1 ) ;
  ~pathLength = Length ( ~path ) ;
  ~url = If ( not IsEmpty ( ~path ) ; Middle ( ~url ; ( ~pathLength + 1 ) ; ~urlLength ) ; ~url ) ;

  // file
  ~pathList = Substitute ( ~path ; "/" ; "¶" ) ;
  ~pathListLast = GetValue ( ~pathList ; ValueCount ( ~pathList ) ) ;
  ~fileName = If ( GetAsBoolean ( PatternCount ( ~pathListLast ; "." ) ) ; ~pathListLast ; "" ) ;

  // query
  ~isQuery = GetAsBoolean ( Left ( ~url ; 1 ) = "?" ) ;
  ~query = GetValue ( Substitute ( ~url ; [ "?" ; "" ] ; [ "#" ; "¶" ] ) ; 1 ) ;
  ~queryLength = Length ( "?" & ~query ) ;
  ~url = If ( ~isQuery ; Middle ( ~url ; ( ~queryLength + 1 ) ; ~urlLength ) ; ~url ) ;

  // fragment
  ~fragment = GetValue ( Substitute ( ~url ; [ "#" ; "¶" ] ) ; 2 ) ;
  ~fragmentLength = Length ( "#" & ~fragment ) ;
  ~url = If ( not IsEmpty ( ~fragment ) ; Middle ( ~url ; ( ~fragmentLength + 1 ) ; ~urlLength ) ; ~url ) ;

  // check
  ~urlIsEmpty = If ( IsEmpty ( ~url ) ; True ; False )

] ;

JSONSetElement ( "" ;
  [ "scheme" ; ~scheme ; JSONString ] ;
  [ "userName" ; ~userName ; JSONString ] ;
  [ "password" ; ~password ; JSONString ] ;
  [ "host" ; ~host ; JSONString ] ;
  [ "port" ; ~port ; JSONString ] ;
  [ "path" ; ~path ; JSONString ] ;
  [ "fileName" ; ~fileName ; JSONString ] ;
  [ "fragment" ; ~fragment ; JSONString ] ;
  [ "query" ; ~query ; JSONString ] ;
  [ "urlIsEmpty" ; ~urlIsEmpty ; JSONBoolean ]
)

) /*Let*/

サンプル

ParseUrl ( "https://username:[email protected]:80/dir1/file.txt?q1=v1&q2=v2#fragment" )

----------

{
  "fileName": "file.txt",
  "fragment": "fragment",
  "host": "bucketname.s3.ap-northeast-1.amazonaws.com",
  "password": "password",
  "path": "/dir1/file.txt",
  "port": "999",
  "query": "q1=v1&q2=v2",
  "scheme": "https",
  "urlIsEmpty": true,
  "userName": "username"
}

ParseUrl ( "https://www.briandunning.com/cf/2627" )

----------

{
  "fileName": "",
  "fragment": "",
  "host": "www.briandunning.com",
  "password": "",
  "path": "/cf/2627",
  "port": "",
  "query": "",
  "scheme": "https",
  "urlIsEmpty": true,
  "userName": ""
}

バグなど見つかりましたら、コメント頂ければと思います。