How to generate base64 digest string in Appian.

Certified Senior Developer

Hello All,

Below is the code snippet from Ruby.

Digest::SHA256.base64digest('')

I need to generate the same in Appian and pass it to the integration header parameter, please let me know if anyone has done this before.

Thank You,

Sneha.

  Discussion posts and replies are publicly visible

Parents Reply Children
  • 0
    Certified Lead Developer
    in reply to lalithap4822
    failing to decode Euro symbol

    Are you talking about the "â" character?  Because that's what it looks like your example string contains.  I'm unclear but for some reason my base64 back-parser is interpreting it as 2 characters instead of 1, which I'll look into - it could be an error in my parser, or some weird exception involving the convergence of character sets or something, i'm not terribly sure yet.

  • +1
    Certified Lead Developer
    in reply to lalithap4822

    After some further research, I found that extended characters (anything past char(127)) are handled specially in standard UTF-8 base64 encoding.  I did a little tampering with my existing rules and came up with the following updates today:

    Encoder:

    /* encode */
    a!localVariables(
      local!text: ri!text,
    
      local!charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
      local!codeArray: code(local!text),
      local!binArray: a!forEach(
        local!codeArray,
        if(
          fv!item > 127,
          if(
            fv!item > 2047,
            if(
              fv!item > 65535,
              dec2bin(fv!item, 21),
              dec2bin(fv!item, 16)
            ),
            dec2bin(fv!item, 11)
          ),
          dec2bin(fv!item, 8)
        )
      ),
      
      local!preProcessBinArray: a!forEach(
        /* encoding UTF-8 self-synchronizing (grumble) ref: https://en.wikipedia.org/wiki/UTF-8#Description */
        items: local!binArray,
        expression: a!match(
          value: len(fv!item), default: fv!item,
          
          equals: 8,
          then: fv!item,
          
          equals: 11,
          then: {
            concat( "110", left(fv!item, 5) ),
            concat( "10", right(fv!item, 6) )
          },
          
          equals: 16,
          then: {
            concat( "1110", left(fv!item, 4) ),
            concat( "10", mid(fv!item, 5, 6) ),
            concat( "10", right(fv!item, 6) )
          },
          
          equals: 21,
          then: {
            concat( "11110", left(fv!item, 3) ),
            concat( "10", mid(fv!item, 4, 6) ),
            concat( "10", mid(fv!item, 10, 6) ),
            concat( "10", right(fv!item, 6) )
          }
        )
      ),
      
      local!bins: concat(local!preProcessBinArray),
      
      local!groupedBins: split(concat(
        a!forEach(
          items: code(local!bins), 
          expression: char(fv!item) & if(and(mod(fv!index, 6) = 0, not(fv!isLast)), "-=-=-", "")
        )
      ), "-=-=-" ),
      local!lastBin: index(local!groupedBins, length(local!groupedBins)),
      local!padding: a!match(
        value: len(local!lastBin),
        equals: 2, then: "==",
        equals: 4, then: "=",
        equals: 6, then: "",
        default: "error condition"
      ),
      local!bindexes: a!forEach(
        items: local!groupedBins,
        expression: if(
          fv!isLast,
          bin2dec(substitute(padright(fv!item, 6), " ", "0")),
          bin2dec(fv!item)
        )
      ) + 1,
    
      local!assembledOutput: concat(a!forEach(items: local!bindexes, expression: charat(local!charset, fv!item))) & local!padding,
    
      local!assembledOutput
    )

    Decoder:

    /* decode */
    a!localVariables(
      local!base64text: ri!base64text,
    
      local!charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
    
      local!charCodes: code(local!base64text),
      /*local!paddingCount: count(wherecontains(code("="), local!charCodes)),*/
      local!unpaddedChars: a!flatten(a!forEach(
        items: local!charCodes,
        expression: if(fv!item = code("="), {}, find(char(fv!item), local!charset)-1)
      )),
      local!rawBins: dec2bin(tointeger(local!unpaddedChars), 6),
    
      local!decodedBins: split(
        concat(
          a!forEach(
            items: code(concat(local!rawBins)),
            expression: if(
              mod(fv!index, 8) = 0,
              char(fv!item) & "-=-=-",
              char(fv!item)
            )
          )
        ),
        "-=-=-"
      ),
    
      
      local!intermediaryDecodedBinList: a!flatten(a!forEach(
        /* update to handle UTF-8 self-synchronizing for extended character-set items. ref: https://en.wikipedia.org/wiki/UTF-8#Description */
        items: local!decodedBins,
        expression: if(
          or(fv!item = "0000", fv!item = "00"),
          {},
          
          a!match(
            value: fv!item, default: fv!value,
    
            /* handle 2-byte items */
            whentrue: left(fv!value, 3) = "110",
            then: concat(right(fv!item, 5), right(local!decodedBins[fv!index+1], 6)),
            whenTrue: or(  /* manually check and skip re-using subsequent ones on following iterations (ugh) */
              and(fv!index >= 2, left(local!decodedBins[fv!index-1], 3) = "110"),
            ),
            then: {},
    
            /* handle 3-byte items */
            whentrue: left(fv!value, 4) = "1110",
            then: concat(right(fv!item, 4), right(local!decodedBins[fv!index+1], 6), right(local!decodedBins[fv!index+2], 6)),
            whenTrue: or(  /* manually check and skip re-using subsequent ones on following iterations (ugh) */
              and(fv!index >= 2, left(local!decodedBins[fv!index-1], 4) = "1110"),
              and(fv!index >= 3, left(local!decodedBins[fv!index-2], 4) = "1110")
            ),
            then: {},
    
            /* handle 4-byte items */
            whentrue: left(fv!value, 5) = "11110",
            then: concat(right(fv!item, 3), right(local!decodedBins[fv!index+1], 6), right(local!decodedBins[fv!index+2], 6), right(local!decodedBins[fv!index+3], 6)),
            whenTrue: or(  /* manually check and skip re-using subsequent ones on following iterations (ugh) */
              and(fv!index >= 2, left(local!decodedBins[fv!index-1], 5) = "11110"),
              and(fv!index >= 3, left(local!decodedBins[fv!index-2], 5) = "11110"),
              and(fv!index >= 4, left(local!decodedBins[fv!index-3], 5) = "11110")
            ),
            then: {}
            
          )
        )
      )),
      
      concat(
        a!forEach(
          local!intermediaryDecodedBinList,
          char(bin2dec(fv!item))
        )
      )
    )

    These work with text containing the euro symbol and other extended character set items to an extent.  Warning that I have *not* tested them very thoroughly for corner cases (like items that exist at character set boundaries) yet, but the results so far match results given by public online base64 encoder tools.