Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

...

Bloco de código
titleAPI REST - idiomas.p
linenumberstrue
collapsetrue
{utp/ut-api.i}

{utp/ut-api-action.i pFindById GET /byid/~* }
{utp/ut-api-action.i pIdiomas GET /translations/~* }
{utp/ut-api-action.i pFindAll GET /~* }

{utp/ut-api-action.i pUpdateById PUT /~* }

{utp/ut-api-action.i pGetMetadata POST /metadata/~* }
{utp/ut-api-action.i pValidateForm POST /validateForm/~* }
{utp/ut-api-action.i pValidateField POST /validateField/~* }
{utp/ut-api-action.i pCreate POST /~* }

{utp/ut-api-action.i pDeleteById DELETE /~* }

{utp/ut-api-notfound.i}

DEFINE TEMP-TABLE ttIdiomas NO-UNDO
    FIELD cod_idioma      LIKE idioma.cod_idioma      SERIALIZE-NAME "codIdioma"
    FIELD des_idioma      LIKE idioma.des_idioma      SERIALIZE-NAME "desIdioma"
    FIELD cod_idiom_padr  LIKE idioma.cod_idiom_padr  SERIALIZE-NAME "codIdiomPadr"
    FIELD dat_ult_atualiz LIKE idioma.dat_ult_atualiz SERIALIZE-NAME "datUltAtualiz"
    FIELD hra_ult_atualiz LIKE idioma.hra_ult_atualiz SERIALIZE-NAME "hraUltAtualiz"
    FIELD id              AS RECID.

/** Procedure que retorna a metadata **/
PROCEDURE pGetMetadata:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    DEFINE VARIABLE oResponse AS JsonAPIResponse NO-UNDO.
    DEFINE VARIABLE oIdiomas  AS JsonArray       NO-UNDO.
    DEFINE VARIABLE oOpts     AS JsonArray       NO-UNDO.
    DEFINE VARIABLE oObj      AS JsonObject      NO-UNDO.
    DEFINE VARIABLE oOpt      AS JsonObject      NO-UNDO.

    ASSIGN oIdiomas = NEW JsonArray().
    
    /* Define a lista de campos a serem apresentados no HTML */
    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'codIdioma').
    oObj:add('label', 'Idioma'"~{~{language~}~}").
    oObj:add('visible', TRUE).
    oObj:add('disable', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).
    
    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'desIdioma').
    oObj:add('label', 'Descrição~{~{description~}~}').
    oObj:add('visible', TRUE).
    oObj:add('required', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).

    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'codIdiomPadr').
    oObj:add('label', 'Idioma Padrão~{~{defaultLanguage~}~}').
    oObj:add('visible', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).

    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'datUltAtualiz').
    oObj:add('label', 'Última Atualização~{~{lastUpdate~}~}').
    oObj:add('visible', TRUE).
    oObj:add('format', 'dd/MM/yyyy').
    oObj:add('disable', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('date')).
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).

    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'hraUltAtualiz').
    oObj:add('label', 'Hora Última Atualização~{~{hourLastUpdate~}~}').
    oObj:add('visible', TRUE).
    oObj:add('disable', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).
    
    // osacoes camposde abaixotela foram adicionados para testes de validacao do formulario
    ASSIGN oOpts = NEW JsonArray().
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Foco CodIdiomaPadrao~{~{languageDefaultFocus~}~}').
    oOpt:add('value', 'focoCodIdiomPadr').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Foco DesIdioma~{~{descriptionFocus~}~}').
    oOpt:add('value', 'FocoDesIdioma').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Desabilita CodIdiomaPadrao~{~{languageDefaultDisable~}~}').
    oOpt:add('value', 'DesabilitaCodIdiomaPadrao').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Habilita CodIdiomaPadrao~{~{languageDefaultEnable~}~}').
    oOpt:add('value', 'HabilitaCodIdiomaPadrao').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Mascara CPF~{~{cpfMask~}~}').
    oOpt:add('value', 'MascaraCPF').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Mascara CNPJ~{~{cnpjMask~}~}').
    oOpt:add('value', 'MascaraCNPJ').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'TrocaValor DesIdioma~{~{changeValueLanguage~}~}').
    oOpt:add('value', 'TrocaValorDesIdioma').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Esconder DesIdioma~{~{languageHide~}~}').
    oOpt:add('value', 'EsconderDesIdioma').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Aparecer DesIdioma~{~{languageShow~}~}').
    oOpt:add('value', 'AparecerDesIdioma').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Mostra Mensagem de Erro~{~{errorMessageShow~}~}').
    oOpt:add('value', 'showErrorMessage').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Muda Label DesIdioma~{~{languageLabelChange~}~}').
    oOpt:add('value', 'mudaLabelDesIdioma').
    oOpts:add(oOpt).

    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'codAcoes').
    oObj:add('label', 'Acoes de Tela~{~{screenActions~}~}').
    oObj:add('visible', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('options', oOpts).
    oObj:add('gridColumns', 12).
    oObj:add('validate', '/api/trn/v1/idiomas/validateField').
    oIdiomas:add(oObj).

    // Informacoes de Tipo de Pessoa
    ASSIGN oOpts = NEW JsonArray().
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Pessoa Fisica~{~{PF~}~}').
    oOpt:add('value', 'f').
    oOpts:add(oOpt).
    ASSIGN oOpt = NEW JsonObject().
    oOpt:add('label', 'Pessoa Juridica~{~{PJ~}~}').
    oOpt:add('value', 'j').
    oOpts:add(oOpt).
    
    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'tipUsuario').
    oObj:add('label', 'Tipo do usuario~{~{userType~}~}').
    oObj:add('visible', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('options', oOpts).
    oObj:add('gridColumns', 6).
    oObj:add('validate', '/api/trn/v1/idiomas/validateField').
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).

    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'codCpfCnpj').
    oObj:add('label', 'CPF/CNPJ~{~{documentOptions~}~}').
    oObj:add('visible', TRUE).
    oObj:add('mask', '999.999.999-99').
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).
    
    // Adiciona o campo ID na lista de campos para a interface HTML
    // Isso facilitara o gerenciamento do registro na interface HTML
    oIdiomas:add(JsonAPIUtils:getIdField()).

    // Adiciona o JsonArray em um JsonObject para enviar para a UPC
    oObj        = NEW JsonObject().
    oObj:add('root', oIdiomas).
    
    // Realiza a chamada da UPC Progress
    {include/i-epcrest.i &endpoint=getMetaData &event=getMetaData &jsonVar=oObj}    

    // Recupera o JsonArray de dentro do JsonObject retornado pela UPC
    oIdiomas = oObj:getJsonArray('root').    
    
    // Retorna a colecao de campos customizados ou nao para a interface HTML
    oResponse   = NEW JsonAPIResponse(oIdiomas).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

/** Procedure que retorna os valores **/
PROCEDURE pFindAll:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    DEFINE VARIABLE oResponse AS JsonAPIResponse NO-UNDO.
    DEFINE VARIABLE oIdiomas  AS JsonArray       NO-UNDO.
    DEFINE VARIABLE oObj      AS JsonObject      NO-UNDO.
    DEFINE VARIABLE oId       AS JsonObject      NO-UNDO.
 
    EMPTY TEMP-TABLE ttIdiomas.

    // Monta a lista de valores dos campos
    FOR EACH idioma NO-LOCK BY idioma.cod_idioma:
        CREATE ttIdiomas.
        BUFFER-COPY idioma TO ttIdiomas.
        // Alimenta o campo ID utilizado pela interface HTML como chave primaria
        ASSIGN ttIdiomas.id = RECID(idioma).
    END.
    
    // Obtem um jsonArray com base no conteudo da temp-table
    oIdiomas    = JsonAPIUtils:convertTempTableToJsonArray(TEMP-TABLE ttIdiomas:HANDLE).

    // Adiciona o JsonArray em um JsonObject para enviar para a UPC
    oObj        = NEW JsonObject().
    oObj:add('root', oIdiomas).
    
    // Realiza a chamada da UPC Progress
    {include/i-epcrest.i &endpoint=findAll &event=findAll &jsonVar=oObj}

    // Recupera o JsonArray de dentro do JsonObject retornado pela UPC
    oIdiomas = oObj:getJsonArray('root').    

    // Retorna a colecao de dados customizados ou nao para a interface HTML
    oResponse   = NEW JsonAPIResponse(oIdiomas).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

/** Procedure que retorna 1 registro pelo ID **/ 
PROCEDURE pFindById:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    DEFINE VARIABLE oRequest   AS JsonAPIRequestParser NO-UNDO.
    DEFINE VARIABLE oResponse  AS JsonAPIResponse      NO-UNDO.
    DEFINE VARIABLE oIdioma    AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oId        AS JsonObject           NO-UNDO.
    
    DEFINE VARIABLE cId        AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE iId        AS INTEGER              NO-UNDO.

    EMPTY TEMP-TABLE ttIdiomas.

    // Le os parametros enviados pela interface HTML
    oRequest = NEW JsonAPIRequestParser(oJsonInput).
    
    // Obtem o ID
    cId = oRequest:getPathParams():getCharacter(2).
    
    // Localiza o registro na tabela IDIOMA pelo ID (recid)
    FIND FIRST idioma 
        WHERE RECID(idioma) = INT(cId)
        NO-LOCK NO-ERROR.
    IF AVAILABLE idioma THEN DO:
        BUFFER-COPY idioma TO ttIdiomas.
        ASSIGN ttIdiomas.id = RECID(idioma).
    END.
    
    // Obtem um jsonArray com base no conteudo da temp-table
    oIdioma     = JsonAPIUtils:convertTempTableFirstItemToJsonObject(TEMP-TABLE ttIdiomas:HANDLE).

    // Realiza a chamada da UPC Progress
    {include/i-epcrest.i &endpoint=findById &event=findById &jsonVar=oIdioma}    
   
    // Retorna o registro customizado ou nao para a interface HTML
    oResponse   = NEW JsonAPIResponse(oIdioma).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

/** Procedure que cria um novo registro na tabela **/
PROCEDURE pCreate:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO. 

    DEFINE VARIABLE oBody          AS JsonObject           NO-UNDO. 
    DEFINE VARIABLE oRequest       AS JsonAPIRequestParser NO-UNDO.
    DEFINE VARIABLE oResponse      AS JsonAPIResponse      NO-UNDO.
    
    DEFINE VARIABLE cCodIdioma     AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE cDesIdioma     AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE cCodIdiomPadr  AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE rIdioma        AS RECID                NO-UNDO.
    DEFINE VARIABLE lCreated       AS LOGICAL              NO-UNDO INITIAL FALSE.
 
    // Le os parametros e os dados enviados pela interface HTML
    oRequest = NEW JsonAPIRequestParser(oJsonInput).
    oBody    = oRequest:getPayload().
    
    // Obtem os demais dados
    cCodIdioma    = oBody:getCharacter("codIdioma") NO-ERROR.
    cDesIdioma    = oBody:getCharacter("desIdioma") NO-ERROR.
    cCodIdiomPadr = oBody:getCharacter("codIdiomPadr") NO-ERROR.

    // Cria o registro na tabela IDIOMA
    DO  TRANSACTION
        ON ERROR UNDO, LEAVE:
        FIND FIRST idioma
            WHERE idioma.cod_idioma = cCodIdioma
            NO-LOCK NO-ERROR.
        IF  NOT AVAILABLE idioma THEN DO:
            CREATE idioma.
            ASSIGN idioma.cod_idioma      = cCodIdioma
                   idioma.des_idioma      = cDesIdioma
                   idioma.cod_idiom_padr  = cCodIdiomPadr
                   idioma.dat_ult_atualiz = TODAY
                   idioma.hra_ult_atualiz = STRING(TIME,"HH:MM:SS")
                   rIdioma                = RECID(idioma)
                   lCreated               = TRUE.
        
            // Realiza a chamada da UPC Progress para a criacao do 
            // registro customizado. Nao utilizaremos o retorno da UPC
            // neste caso. 
            {include/i-epcrest.i &endpoint=create &event=afterCreate &jsonVar=oBody}    
        
            RELEASE idioma.
        END.
    END.

    // Retorna o ID e se foi criado com sucesso
    oBody = NEW JsonObject().
    oBody:add('id', rIdioma).
    oBody:add('created', (IF lCreated THEN 'OK' ELSE 'NOK')).   

    // Retorna o oBody montado para a interface HTML
    oResponse   = NEW JsonAPIResponse(oBody).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

/** Procedure que atualiza o conteudo do registro pelo ID **/ 
PROCEDURE pUpdateById:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    DEFINE VARIABLE oBody          AS JsonObject           NO-UNDO. 
    DEFINE VARIABLE oRequest       AS JsonAPIRequestParser NO-UNDO.
    DEFINE VARIABLE oResponse      AS JsonAPIResponse      NO-UNDO.
    DEFINE VARIABLE oIdioma        AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oId            AS JsonObject           NO-UNDO.
    DEFINE VARIABLE cId            AS CHARACTER            NO-UNDO.
    
    DEFINE VARIABLE cCodIdioma     AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE cDesIdioma     AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE cCodIdiomPadr  AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE datUltAtualiz  AS DATE                 NO-UNDO.
    DEFINE VARIABLE hraUltAtualiz  AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE lUpdated       AS LOGICAL              NO-UNDO INITIAL FALSE.

    // Le os parametros e os dados enviados pela interface HTML
    oRequest = NEW JsonAPIRequestParser(oJsonInput).
    oBody    = oRequest:getPayload().
   
    // Obtem o ID
    cId      = oRequest:getPathParams():getCharacter(1).

    // Obtem os demais dados
    cCodIdioma    = oBody:getCharacter("codIdioma") NO-ERROR.
    cDesIdioma    = oBody:getCharacter("desIdioma") NO-ERROR.
    cCodIdiomPadr = oBody:getCharacter("codIdiomPadr") NO-ERROR.
    
    // Atualiza o registro na tabela IDIOMA pelo ID (recid)
    DO  TRANSACTION
        ON ERROR UNDO, LEAVE:
        FIND FIRST idioma 
            WHERE RECID(idioma) = INT(cId)
            EXCLUSIVE-LOCK NO-ERROR.
        IF  AVAILABLE idioma THEN DO:
            ASSIGN idioma.des_idioma      = cDesIdioma
                   idioma.cod_idiom_padr  = cCodIdiomPadr
                   idioma.dat_ult_atualiz = TODAY
                   idioma.hra_ult_atualiz = STRING(TIME,"HH:MM:SS")
                   lUpdated               = TRUE.
            
            // Realiza a chamada da UPC Progress para atualizar o registro
            // na tabela cutomizada ou nao. Nao utilizaremos o retorno da UPC
            // neste caso. 
            {include/i-epcrest.i &endpoint=update &event=afterUpdate &jsonVar=oBody}    
        END.
    END.
   
    // Retorna o ID e se foi atualizado com sucesso
    oBody = NEW JsonObject().
    oBody:add('id', cId).
    oBody:add('updated', (IF lUpdated THEN 'OK' ELSE 'NOK')).   

    // Retorna o oBody montado para a interface HTML
    oResponse   = NEW JsonAPIResponse(oBody).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

/** Procedure que atualiza o conteudo do registro pelo ID **/ 
PROCEDURE pDeleteById:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    DEFINE VARIABLE oObj           AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oRequest       AS JsonAPIRequestParser NO-UNDO.
    DEFINE VARIABLE oResponse      AS JsonAPIResponse      NO-UNDO.
    DEFINE VARIABLE oArray         AS JsonArray            NO-UNDO.
    DEFINE VARIABLE oIdioma        AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oId            AS JsonObject           NO-UNDO.
    DEFINE VARIABLE cId            AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE lDeleted       AS LOGICAL              NO-UNDO INITIAL FALSE.
    DEFINE VARIABLE ix             AS INTEGER              NO-UNDO.

    // Le os parametros enviados pela interface HTML
    oRequest = NEW JsonAPIRequestParser(oJsonInput).
    
    // Eliminacao de registro individual
    IF  oRequest:getPathParams():length > 0 THEN DO:
        // Obtem o ID
        cId = oRequest:getPathParams():getCharacter(1).
        
        RUN piDeleteRecord (cId).
        ASSIGN lDeleted = (RETURN-VALUE = "OK").
    END.
    ELSE DO:
        // Eliminacao de registros em lote
        // Obtem a lista de IDs diretamente do oJsonInput onde vem um JsonArray
        // oArray = oJsonInput:getJsonArray('payload').
        oArray = oRequest:getPayloadArray().
        DO  ix = 1 TO oArray:length:
            oObj = oArray:getJsonObject(ix).
            cId = STRING(oObj:getInteger('id')).
        
            RUN piDeleteRecord (cId).
            IF  lDeleted = FALSE THEN 
                ASSIGN lDeleted = (RETURN-VALUE = "OK").
        END.
    END.
    
    // Retorna o ID e se foi criado com sucesso
    oObj = NEW JsonObject().
    IF  oRequest:getPathParams():length > 0 THEN DO:
        oObj:add('id', cId).
    END.
    oObj:add('deleted', (IF lDeleted THEN 'OK' ELSE 'NOK')).
    
    // Retorna o oBody montado para a interface HTML
    oResponse   = NEW JsonAPIResponse(oObj).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

PROCEDURE piDeleteRecord:
    DEFINE INPUT PARAMETER cId AS CHARACTER NO-UNDO.

    DEFINE VARIABLE oObj           AS JsonObject           NO-UNDO.
    DEFINE VARIABLE lDeleted       AS LOGICAL NO-UNDO INITIAL FALSE.

    LOG-MANAGER:WRITE-MESSAGE("Eliminando registro -> " + cId, ">>>>>").

    // Elimina o registro na tabela IDIOMA pelo ID (recid)
    DO  TRANSACTION
        ON ERROR UNDO, LEAVE:
        FIND FIRST idioma 
            WHERE RECID(idioma) = INT(cId)
            EXCLUSIVE-LOCK NO-ERROR.
        IF AVAILABLE idioma THEN DO:
            // Monta a chave estrangeira para enviar para UPC
            // poder elominar o registro da tabela customizada
            oObj = NEW JsonObject().
            oObj:add('codIdioma', idioma.cod_idioma).
            
            // Realiza a chamada da UPC Progress para a eliminacao do 
            // registro customizado. Nao utilizaremos o retorno da UPC
            // neste caso. 
            {include/i-epcrest.i &endpoint=delete &event=beforeDelete &jsonVar=oObj}    

            DELETE idioma.
           
            ASSIGN lDeleted = TRUE.
        END.
    END.
    
    RETURN (IF lDeleted THEN "OK" ELSE "NOK").
END PROCEDURE.

PROCEDURE pValidateForm:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    DEFINE VARIABLE oRequest   AS JsonAPIRequestParser NO-UNDO.
    DEFINE VARIABLE oResponse  AS JsonAPIResponse      NO-UNDO.
    DEFINE VARIABLE oBody      AS JsonObject           NO-UNDO.
    DEFINE VARIABLE cProp      AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE oValue     AS JsonObject           NO-UNDO.
    DEFINE VARIABLE cValue     AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE cId        AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE oNewValue  AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oNewFields AS JsonArray            NO-UNDO.
    DEFINE VARIABLE cFocus     AS CHARACTER            NO-UNDO.

    DEFINE VARIABLE oRet       AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oObj       AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oMessages  AS JsonArray            NO-UNDO.

    oRequest = NEW JsonAPIRequestParser(oJsonInput).
    oBody    = oRequest:getPayload().
   
    // obtem o nome da propriedade que ocorreu o LEAVE para validacao
    cProp      = oBody:getCharacter("property")     NO-ERROR.
    oValue     = oBody:getJsonObject("value")       NO-ERROR.
    cValue     = STRING(oValue:GetCharacter(cProp)) NO-ERROR.
    cId        = oValue:getCharacter("id")          NO-ERROR.
    
    /* Recebemos do HTML o JSON abaixo
    {
        "property": "codAcoes",
        "value": {
            "codIdiomPadr": "01 PortuguêsPortuguˆs",
            "codIdioma": "12345678",
            "desIdioma": "12345678901234567890",
            "hraUltAtualiz": "",
            "datUltAtualiz": null,
            "id": 6,
            "codAcoes": "FocoDesIdioma"
        }
    }
    */

    // Novas Acoes sobre os campos da tela
    
    // oNewValue guarda os valores a serem especificados para os campos
    ASSIGN oNewValue = NEW JsonObject().
    
    // oNewFields guarda a lista de campos que serao alterados/modificados
    ASSIGN oNewFields = NEW JsonArray().
    
    // cFocus especifica em qual campo sera feito o focus
    ASSIGN cFocus = cProp.

    // oMessages guarda as mensagens de retorno formato 
    // { code: '00', message: 'texto', detailedMessage: 'detalhes da mensagem' }
    ASSIGN oMessages = NEW JsonArray().
   
    CASE cProp:
        WHEN "codAcoes" THEN DO:
            CASE cValue:
                WHEN 'focoCodIdiomPadr' THEN DO:
                    // setamos o focus para o campo desejado
                    ASSIGN cFocus = 'codIdiomPadr'.
                END.
                WHEN 'FocoDesIdioma' THEN DO:
                    // setamos o focus para o campo desejado
                    ASSIGN cFocus = 'desIdioma'.
                END.
                WHEN 'DesabilitaCodIdiomaPadrao' THEN DO:
                    // criamos um novo field para desabilitar
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('property', 'codIdiomPadr').
                    oObj:add('disabled', TRUE).
                    oNewFields:add(oObj).
                END.
                WHEN 'HabilitaCodIdiomaPadrao' THEN DO:
                    // criamos um novo field para habilitar
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('property', 'codIdiomPadr').
                    oObj:add('disabled', FALSE).
                    oNewFields:add(oObj).
                END.
                WHEN 'MascaraCPF' THEN DO:
                    // IMPORTANTE:
                    // Quando alteramos o valor do radio-set tipUsuario por aqui,
                    // O value-changed dele que especifica qual a mascara
                    // seráser  utilizada NAO sera disparado, pois ele é dinamico e 
                    // estamos validando o campo codAcoes, sendo necessario 
                    // fazermos a formatacao da mascara aqui tambem. 
                    // A mesma regra é valida para o CNPJ
                    
                    // mudamos os valores dos campos desejados
                    oNewValue:add('tipUsuario', 'f').
                    oNewValue:add('codCpfCnpj', FILL('0',11)).

                    // criamos um novo field para mudar a mascara
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('property', 'codCpfCnpj').
                    oObj:add('mask', '999.999.999-99').
                    oNewFields:add(oObj).
                END.
                WHEN 'MascaraCNPJ' THEN DO:
                    // IMPORTANTE:
                    // Quando alteramos o valor do radio-set tipUsuario por aqui,
                    // O value-changed dele que especifica qual a mascara
                    // seráser  utilizada NAO sera disparado, pois ele é dinamico e 
                    // estamos validando o campo codAcoes, sendo necessario 
                    // fazermos a formatacao da mascara aqui tambem. 
                    // A mesma regra é valida para o CPF

                    // alteramos os valores dos campos desejados
                    oNewValue:add('tipUsuario', 'j').
                    oNewValue:add('codCpfCnpj', FILL('0',15)).

                    // criamos um novo field para mudar a mascara
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('property', 'codCpfCnpj').
                    oObj:add('mask', '99.999.999/9999-99').
                    oNewFields:add(oObj).
                END.
                WHEN 'TrocaValorDesIdioma' THEN DO:
                    // alteramos o conteudo de um campo qualquer
                    oNewValue:add('desIdioma', "Valor retornado do backend de validacao").
                END.
                WHEN 'EsconderDesIdioma' THEN DO:
                    // criamos um novo field para tornar invisivel o campo
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('property', 'desIdioma').
                    oObj:add('visible', FALSE).
                    oNewFields:add(oObj).
                END.
                WHEN 'AparecerDesIdioma' THEN DO:
                    // criamos um novo field para tornar visivel o campo
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('property', 'desIdioma').
                    oObj:add('visible', TRUE).
                    oNewFields:add(oObj).
                END.
                WHEN 'mudaLabelDesIdioma' THEN DO:
                    // criamos um novo field para mudar o label
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('property', 'desIdioma').
                    oObj:add('label', 'Label alterado da descricao').
                    oNewFields:add(oObj).
                END.
                WHEN 'showErrorMessage' THEN DO:
                    // criamos uma mensagem de erro
                    ASSIGN oObj = NEW JsonObject().
                    oObj:add('code', '33').
                    oObj:add('message', 'A Descricao do idioma nao foi preenchida corretamente'). 
                    oObj:add('detailedMessage', 'Detalhe da mensagem de erro').
                    oMessages:add(oObj).
                END.
            END CASE.
        END.
        WHEN "tipUsuario" THEN DO:
            // setamos o focus para o campo desejado
            ASSIGN cFocus = 'codCpfCnpj'.

            // criamos um field para mudar o informar o campo e a nova mascara
            ASSIGN oObj = NEW JsonObject().
            oObj:add('property', "codCpfCnpj").
            IF  cValue = "j" THEN DO:
                // é definido um novo valor para o CNPJ
                oNewValue:add('codCpfCnpj', FILL('0',15)).
                // é alterado o formato da mascara de edicao
                oObj:add('mask', '99.999.999/9999-99').
            END.
            IF  cValue = "f" THEN DO:
                // é definido um novo valor para o CPF
                oNewValue:add('codCpfCnpj', FILL('0',11)).
                // é alterado o formato da mascara de edicao
                oObj:add('mask', '999.999.999-99').
            END.
            oNewFields:add(oObj).
        END.
    END CASE.
    
    ASSIGN oRet = NEW JsonObject().
    // value -> contem todos os valores dos campos de tela
    oRet:add('value', oNewValue).
    // fields -> contem a lista de campos com suas novas propriedades
    oRet:add('fields', oNewFields).
    // focus -> especifica em qual campo o cursor vai ficar posicionado
    oRet:add('focus', cFocus).
    // _messages -> contem uma lista de mensagens que vao aparecer como notificacoes
    oRet:add('_messages', oMessages).
    
    // encapsulamos o retorno para enviar para a UPC
    oObj = NEW JsonObject().
    oObj:add("property", cProp).
    oObj:add("originalValues", oValue).
    oObj:add("root", oRet).

    // Realiza a chamada da UPC Progress
    {include/i-epcrest.i &endpoint=validateForm &event=validateForm &jsonVar=oObj}    

    // obtem o retorno customizado, onde o mesmo foi alterado e retornado somentena 
tag    // o conteudo da tag returnroot 
    oRet = oObj:getJsonObject("root").

    /* JSON de retorno para o HTML      
    value: {
      desIdioma: 'teste de escrita',
      hraUltAtualiz: '17:18:19'
    },
    fields: [
      {
        property: 'codCpfCnpj', 
        mask: '99.999.999/9999-99' 
      }
    ],
    focus: 'hraUltAtualiz',
    _messages: [ 
        { 
            code: '01', 
            message: 'Mensagem do erro que aconteceu', 
            detailedMessage: 'detalhes do erro acontecido' 
        } 
    ]
    */
    
    // Retorna a colecao de campos customizados ou nao para a interface HTML
    oResponse   = NEW JsonAPIResponse(oRet).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

PROCEDURE pValidateField:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    DEFINE VARIABLE oRequest   AS JsonAPIRequestParser NO-UNDO.
    DEFINE VARIABLE oResponse  AS JsonAPIResponse      NO-UNDO.
    DEFINE VARIABLE oBody      AS JsonObject           NO-UNDO.
    DEFINE VARIABLE cProp      AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE oValue     AS JsonObject           NO-UNDO.
    DEFINE VARIABLE cValue     AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE cId        AS CHARACTER            NO-UNDO.
    DEFINE VARIABLE oNewValue  AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oNewField  AS JsonObject           NO-UNDO.
    DEFINE VARIABLE lFocus     AS LOGICAL              NO-UNDO INITIAL FALSE.

    DEFINE VARIABLE oRet       AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oObj       AS JsonObject           NO-UNDO.
    DEFINE VARIABLE oMessages  AS JsonArray            NO-UNDO.

    oRequest = NEW JsonAPIRequestParser(oJsonInput).
    oBody    = oRequest:getPayload().
   
    // obtem o nome da propriedade que ocorreu o LEAVE para validacao
    cProp      = oBody:getCharacter("property")     NO-ERROR.
    cValue     = oBody:getCharacter("value")        NO-ERROR.
    
    /* Recebemos do HTML o JSON abaixo
    {
        "property": "codAcoes",
        "value": "FocoDesIdioma"
    }
    */

    // Novas Acoes sobre os campos da tela
    
    // oNewField guarda o objeto que sera alterado/modificado
    ASSIGN oNewField = NEW JsonObject().
    
    // oMessages guarda as mensagens de retorno formato 
    // { code: '00', message: 'texto', detailedMessage: 'detalhes da mensagem' }
    ASSIGN oMessages = NEW JsonArray().

    IF  cProp = "tipUsuario" THEN DO:
        ASSIGN lFocus = TRUE.
        
        oNewField:add('property', "codCpfCnpj").
        IF  cValue = "j" THEN DO:
            // é alterado o formato da mascara de edicao
            oNewField:add('mask', '99.999.999/9999-99').
        END.
        IF  cValue = "f" THEN DO:
            // é alterado o formato da mascara de edicao
            oNewField:add('mask', '999.999.999-99').
        END.

        ASSIGN oObj = NEW JsonObject().
        oObj:add('code', '33').
        oObj:add('message', 'A mascara do CPF/CNPJ foi ajustada'). 
        oObj:add('detailedMessage', 'Ocorreu um ajusta na mascara do CPF/CNPJ, favor verificar se os dados estao corretos').
        oMessages:add(oObj).
    END.
    
    ASSIGN oRet = NEW JsonObject().
    // value -> contem todos os valores dos campos de tela
    oRet:add('value', cValue).
    // field -> contem os novos atributos do campo atual
    oRet:add('field', oNewField).
    // focus -> especifica se o campo recebe o focu
    oRet:add('focus', lFocus).
    // _messages -> contem uma lista de mensagens que vao aparecer como notificacoes
    oRet:add('_messages', oMessages).
    
    // encapsulamos o retorno para enviar para a UPC
    oObj = NEW JsonObject().
    oObj:add("property", cProp).
    oObj:add("root", oRet).

    // Realiza a chamada da UPC Progress
    {include/i-epcrest.i &endpoint=validateField &event=validateField &jsonVar=oObj}    

    // obtem o retorno customizado, onde o mesmo foi alterado e retornado somente 
    // o conteudo da tag return
    oRet = oObj:getJsonObject("root").

    /* JSON de retorno para o HTML      
    value: 'teste de escrita',
    field: {
        mask: '99.999.999/9999-99',
        required: true 
    },
    focus: true,
    _messages: [
        {
            code: '01', 
            message: 'Mensagem do erro que aconteceu', 
            detailedMessage: 'detalhes do erro acontecido' 
        }
    ]
    */
    
    // Retorna a colecao de campos customizados ou nao para a interface HTML
    oResponse   = NEW JsonAPIResponse(oRet).
    oJsonOutput = oResponse:createJsonResponse().
END PROCEDURE.

/**
    Recupera as literais
*/
PROCEDURE pIdiomas:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject    NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject    NO-UNDO.
    
    // Realiza a chamada da UPC Progress
    {include/i-epcrest.i &endpoint=i18n &event=i18n &jsonVar=oJsonInput}
    
    ASSIGN oJsonOutput = oJsonInput.
END PROCEDURE.


/* fim */


Programa UPC:

Abaixo temos um exemplo de uma UPC criada para a API REST:

Bloco de código
titleExemplo de UPC da API REST - idiomas_upc.p
linenumberstrue
collapsetrue
/**************************************************************************
** idiomas_upc.p - Exemplo de epc para Endpoints REST 
** 18/05/2020 - Menna - Criado exemplo
***************************************************************************/

USING PROGRESS.json.*.
USING PROGRESS.json.ObjectModel.*.
USING com.totvs.framework.api.*.

DEFINE INPUT        PARAMETER pEndPoint AS CHARACTER  NO-UNDO.
DEFINE INPUT        PARAMETER pEvent    AS CHARACTER  NO-UNDO.
DEFINE INPUT        PARAMETER pAPI      AS CHARACTER  NO-UNDO.
DEFINE INPUT-OUTPUT PARAMETER jsonIO    AS JSONObject NO-UNDO.

DEFINE VARIABLE jObj            AS JsonObject NO-UNDO.
DEFINE VARIABLE oOriginalValues AS JSonObject NO-UNDO.
DEFINE VARIABLE oReturn         AS JSonObject NO-UNDO.
DEFINE VARIABLE oValues         AS JSonObject NO-UNDO.
DEFINE VARIABLE oFieldObj       AS JSonObject NO-UNDO.

DEFINE VARIABLE oFields         AS JSonArray  NO-UNDO.
DEFINE VARIABLE oMessages       AS JSonArray  NO-UNDO.

DEFINE VARIABLE ix              AS INTEGER    NO-UNDO.
DEFINE VARIABLE iTot            AS INTEGER    NO-UNDO.

DEFINE VARIABLE cCodIdioma      AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cCodUsuario     AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cNomUsuario     AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cCodDialet      AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cProp           AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cFocus          AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cOriginalValue  AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cValue          AS CHARACTER  NO-UNDO.

DEFINE VARIABLE lFocus          AS LOGICAL    NO-UNDO INITIAL FALSE.

/* ***************************  Main Block  *************************** */

LOG-MANAGER:WRITE-MESSAGE("UPC EndPoint = " + pEndPoint, ">>>>").
LOG-MANAGER:WRITE-MESSAGE("UPC Event = " + pEvent, ">>>>").

// Carrega as definicoes dos campos customizados da tabela
IF  pEndPoint = "getMetaData"
AND pEvent    = "getMetaData" THEN DO ON STOP UNDO, LEAVE:
    RUN piGetMetaData.
END.

// Carrega os valores dos campos customizados das tabelas
IF  pEndPoint = "findAll"
AND pEvent    = "findAll" THEN DO ON STOP UNDO, LEAVE:
    RUN piFindAll.
END.

IF  pEndPoint = "findById"
AND pEvent    = "findById" THEN DO ON STOP UNDO, LEAVE:
    RUN piFindById.
END.

IF  pEndPoint = "create"
AND pEvent    = "afterCreate" THEN DO ON STOP UNDO, LEAVE:
    RUN piCreate.
END.

IF  pEndPoint = "update"
AND pEvent    = "afterUpdate" THEN DO ON STOP UNDO, LEAVE:
    RUN piUpdate.
END.

IF  pEndPoint = "delete"
AND pEvent    = "beforeDelete" THEN DO ON STOP UNDO, LEAVE:
    RUN piDelete.
END.

IF  pEndPoint = "validateForm"
AND pEvent    = "validateForm" THEN DO ON STOP UNDO, LEAVE:
    RUN piValidateForm.
END.

IF  pEndPoint = "validateField"
AND pEvent    = "validateField" THEN DO ON STOP UNDO, LEAVE:
    RUN piValidateField.
END.

RETURNIF  pEndPoint = "OKi18n".


AND pEvent    = "i18n" THEN DO ON STOP UNDO, LEAVE:
    RUN piI18N.
END.

RETURN "OK".

PROCEDURE piGetMetaData: 
    // Obtem a lista de campos e valores    
    ASSIGN oFields = jsonIO:getJsonArray('root').

    // Cria os novos campos na lista
    ASSIGN jObj = NEW JsonObject().
    jObj:add('divider', "Itens da UPC").
    jObj:add('property', 'codUsuario').
    jObj:add('label', 'Usuário~{~{user~}~}').
    jObj:add('visible', TRUE).
    jObj:add('required', TRUE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    jObj:add('gridColumns', 6).
    oFields:add(jObj).
    
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'nomUsuario').
    jObj:add('label', 'Nome~{~{name~}~}').
    jObj:add('visible', TRUE).
    jObj:add('required', TRUE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    jObj:add('gridColumns', 6).
    oFields:add(jObj).

    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'codDialet').
    jObj:add('label', 'Dialeto~{~{dialect~}~}').
    jObj:add('visible', FALSE). // <- Remove o item da tela de todos seus correspondentes (Form, View, Table)
    jObj:add('required', TRUE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    jObj:add('gridColumns', 6).
    oFields:add(jObj).
    
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'testeValidacaoRegEx').
    jObj:add('label', 'Teste Validação RegEx~{~{regexTestValidation~}~}').
    jObj:add('gridColumns', 6).
    jObj:add('pattern', "[0-9]~{2~}"). // <- Validacao RegEx
    jObj:add('errorMessage', 'ObrigatórioObrigat¢rio mínimom¡nimo 2 númerosn£meros consecutivos.').
    oFields:add(jObj).

    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'numberRangeValidate').
    jObj:add('label', 'Aplicação de máscara CPF~{~{cpfMaskApply~}~}').
    jObj:add('mask', '999.999.999-99').  // <-- Mascara CPF
    jObj:add('visible', TRUE).
    jObj:add('required', FALSE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    jObj:add('gridColumns', 6).
    oFields:add(jObj).
    
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'numberValidate').
    jObj:add('label', 'Somente números~{~{onlyNumbers~}~}').
    jObj:add('visible', TRUE).
    jObj:add('required', FALSE).
    jObj:add('minValue', 1).
    jObj:add('maxValue', 9).
    jObj:add('errorMessage', 'Somente númerosn£meros de 1 a 9'). // <- Mensagem de erro 1-9
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('integer')). // <- Restringe a digitacao somente numeros
    jObj:add('gridColumns', 6).
    oFields:add(jObj).
    
    // Retorna a nova lista com os campos customizados
    jsonIO:Set("root", oFields).
END PROCEDURE.

PROCEDURE piFindAll:
    // Obtem a lista de campos e valores    
    ASSIGN oFields = jsonIO:getJsonArray('root').

    LOG-MANAGER:WRITE-MESSAGE("UPC FINDALL", ">>>>").

    FIND FIRST usuar_mestre NO-LOCK NO-ERROR.

    // Armazena o tamanho da lista em variavel para evitar LOOP devido a adicionar novos itens na lista
    ASSIGN iTot = oFields:length.

    DO  ix = 1 TO iTot:
        ASSIGN jObj = oFields:GetJsonObject(ix).
        
        // Alimenta os novos dados
        IF  AVAILABLE usuar_mestre THEN DO:
            jObj:add('codUsuario', usuar_mestre.cod_usuario) NO-ERROR.
            jObj:add('nomUsuario', usuar_mestre.nom_usuario) NO-ERROR.
            jObj:add('codDialet', usuar_mestre.cod_dialet) NO-ERROR.
        END.
        
        // Atualiza o objeto na lista
        oFields:set(ix, jObj).
        
        FIND NEXT usuar_mestre NO-LOCK NO-ERROR.
    END.

    // Retorna o json ROOT a lista nova com novos dados customizados 
    jsonIO:Set("root", oFields).
END PROCEDURE.

PROCEDURE piFindById:
    // Obtem as informacoes necessarias da API para retornar dados    
    cCodIdioma  = jsonIO:getCharacter("codIdioma"). // chave estrangeira

    LOG-MANAGER:WRITE-MESSAGE("UPC FINDBYID cod_idioma= " + cCodIdioma, ">>>>").

    // Adiciona os valores da tabela customizada no retorno
    FIND FIRST usuar_mestre NO-LOCK NO-ERROR.
    IF  AVAILABLE usuar_mestre THEN DO:
        jsonIO:add('codUsuario', usuar_mestre.cod_usuario) NO-ERROR.
        jsonIO:add('nomUsuario', usuar_mestre.nom_usuario) NO-ERROR.
        jsonIO:add('codDialet', usuar_mestre.cod_dialet) NO-ERROR.
    END.
END PROCEDURE.

PROCEDURE piCreate:
    // Obtem as informacoes necessarias da API para criacao do registro    
    cCodIdioma  = jsonIO:getCharacter("codIdioma") NO-ERROR. // chave estrangeira
    cCodUsuario = jsonIO:getCharacter("codUsuario") NO-ERROR.
    cNomUsuario = jsonIO:getCharacter("nomUsuario") NO-ERROR.
    cCodDialet  = jsonIO:getCharacter("codDialet") NO-ERROR.

    LOG-MANAGER:WRITE-MESSAGE("UPC CREATE cod_idioma= " + cCodIdioma, ">>>>").
    LOG-MANAGER:WRITE-MESSAGE("UPC CREATE cod_usuario= " + cCodUsuario, ">>>>").
    
    // logica de CREATE
    /* Em comentario a logica para nao criar registros desnecessariamente
    FIND FIRST usuar_mestre
        WHERE usuar_mestre.cod_usuario = cCodUsuario
        EXCLUSIVE-LOCK NO-ERROR.
    IF  NOT AVAILABLE usuar_mestre THEN DO:
        ASSIGN usuar_mestre.nom_usuario = cNomUsuario
               usuar_mestre.cod_dialet  = cCodDialet. 
    END.
    */
END PROCEDURE.

PROCEDURE piUpdate:
    // Obtem as informacoes necessarias da API para atualizacao    
    cCodIdioma  = jsonIO:getCharacter("codIdioma") NO-ERROR. // chave estrangeira
    cCodUsuario = jsonIO:getCharacter("codUsuario") NO-ERROR.
    cNomUsuario = jsonIO:getCharacter("nomUsuario") NO-ERROR.
    cCodDialet  = jsonIO:getCharacter("codDialet") NO-ERROR.
    
    LOG-MANAGER:WRITE-MESSAGE("UPC UPDATE cod_idioma= " + cCodIdioma, ">>>>").
    LOG-MANAGER:WRITE-MESSAGE("UPC UPDATE cod_usuario= " + cCodUsuario, ">>>>").

    // logica de UPDATE
    /* Em comentario a logica para nao alterar tabelas desnecessariamente
    FIND FIRST usuar_mestre
        WHERE usuar_mestre.cod_usuario = cCodUsuario
        EXCLUSIVE-LOCK NO-ERROR.
    IF  AVAILABLE usuar_mestre THEN DO:
        ASSIGN usuar_mestre.nom_usuario = cNomUsuario
               usuar_mestre.cod_dialet  = cCodDialet. 
    END.
    */
END PROCEDURE.

PROCEDURE piDelete:
    // obtem as informacoes necessarias da API para eliminacao    
    cCodIdioma  = jsonIO:getCharacter("codIdioma"). // chave estrangeira
    
    LOG-MANAGER:WRITE-MESSAGE("UPC DELETE cod_idioma= " + cCodIdioma, ">>>>").

    // logica de DELETE
    /* Em comentario a logica para nao eliminar o registro desnecessariamente
    FIND FIRST usuar_mestre
        WHERE usuar_mestre.cod_usuario = cCodUsuario
        EXCLUSIVE-LOCK NO-ERROR.
    IF  AVAILABLE usuar_mestre THEN DO:
        delete usuar_mestre.
    END.
    */
END PROCEDURE.

PROCEDURE piValidateForm:
    cProp = jsonIO:getCharacter("property") NO-ERROR. // o cProp contem o nome da propriedade que esta sendo validada
    oOriginalValues = jsonIO:getJsonObject("originalValues") NO-ERROR. // obtem os valores dos campos que vieram da tela html

    LOG-MANAGER:WRITE-MESSAGE("UPC ValidateForm property= " + cProp, ">>>>").

    oReturn = jsonIO:getJsonObject("root") NO-ERROR. // obtem o retorno que sera enviado para a tela html
    oValues = oReturn:getJsonObject("value") NO-ERROR. // obtem os valores dos campos ja ajustados
    oFields = oReturn:getJsonArray("fields") NO-ERROR. // obtem as propriedades dos campos a serem alteradas
    cFocus = oReturn:getCharacter("focus") NO-ERROR. // obtem o campo de focus a ser retornado para a tela html
    oMessages = oReturn:getJsonArray("_messages") NO-ERROR. // obtem as mensagens a serem retornados para a tela html
    
    /* Exemplo de JSON que veio para a UPC
    { 
        property: 'codAcao',
        originalValues: {
            "codIdiomPadr": "01 PortuguêsPortuguˆs",
            "codIdioma": "12345678",
            "desIdioma": "12345678901234567890",
            "hraUltAtualiz": "",
            "datUltAtualiz": null,
            "id": 6,
            "codAcoes": "FocoDesIdioma"
        },
        returnroot: {
            value: {
              desIdioma: 'teste de escrita',
              hraUltAtualiz: '17:18:19'
            },
            fields: [
              {
                property: 'codCpfCnpj', 
                mask: '99.999.999/9999-99' 
              }
            ],
            focus: 'hraUltAtualiz',
            _messages: [ 
                { 
                    code: '01', 
                    message: 'Mensagem do erro que aconteceu', 
                    detailedMessage: 'detalhes do erro acontecido' 
                } 
            ]
        }
    }
    */

    IF cProp = "desIdioma" THEN DO:
        cCodIdioma  = oOriginalValues:getCharacter("codIdioma"). // chave estrangeira
        IF  cCodIdioma = "12345678" THEN DO:
            oValues:add("desIdioma", "Valor customizado na UPC").
            oValues:add("hraUltAtualiz", "17:18:19").
            
            // criamos um novo field para desabilitar
            ASSIGN jObj = NEW JsonObject().
            jObj:add('property', 'codIdiomPadr').
            jObj:add('disabled', TRUE).
            oFields:add(jObj).
            
            ASSIGN cFocus = "desIdioma".
            
            ASSIGN jObj = NEW JsonObject().
            jObj:add('code', '44').
            jObj:add('message', 'A UPC alterou algumas caracteristica da tela.'). 
            jObj:add('detailedMessage', 'Na execuçãoexecu‡Æo da UPC, houveram alteraçõesaltera‡äes nos campos de tela.').
            oMessages:add(jObj).
        END.
    END.
    
    /* Exemplo de JSON de retorno para o HTML      
    value: {
      desIdioma: 'Valor customizado na UPC'
      hraUltAtualiz: '17:18:19'
    },
    fields: [
      {
        property: 'codCpfCnpj', 
        mask: '99.999.999/9999-99' 
      }
    ],
    focus: 'hraUltAtualiz',
    _messages: [ 
        { 
            code: '01', 
            message: 'Mensagem do erro que aconteceu', 
            detailedMessage: 'detalhes do erro acontecido' 
        } 
    ]
    */

    // atribui os valores de volta para a tela HTML
    jsonIO = NEW JSonObject().
    
    oReturn = NEW JSonObject().
    oReturn:add("value", oValues). // seta os valores dos campos ja ajustados
    oReturn:add("fields", oFields). // seta as propriedades dos campos a serem alteradas
    oReturn:add("focus", cFocus). // seta o campo de focus a ser retornado para a tela html
    oReturn:add("_messages", oMessages). // seta as mensagens a serem retornadas para a tela html

    jsonIO:add("root", oReturn).
END PROCEDURE.

PROCEDURE piValidateField:
    cProp = jsonIO:getCharacter("property") NO-ERROR. // o cProp contem o nome da propriedade que esta sendo validada

    LOG-MANAGER:WRITE-MESSAGE("UPC ValidateField property= " + cProp, ">>>>").

    oReturn = jsonIO:getJsonObject("root") NO-ERROR. // obtem o retorno que sera enviado para a tela html
    cValue = oReturn:getCharacter("value") NO-ERROR. // pega o novo valor do campo atual
    oFieldObj = oReturn:getJsonObject("field") NO-ERROR. // obtem as propriedades dos campos a serem alteradas
    lFocus = oReturn:getLogical("focus") NO-ERROR. // obtem se o focus ficara sobre o mesmo campo ao retornar para a tela html
    oMessages = oReturn:getJsonArray("_messages") NO-ERROR. // obtem as mensagens a serem retornados para a tela html
    
    /* Exemplo de JSON que veio para a UPC
    { 
        property: 'codAcao',
        returnroot: {
            value: '',
            field: {
                mask: '99.999.999/9999-99' 
            },
            focus: false,
            _messages: [ 
                { 
                    code: '01', 
                    message: 'Mensagem do erro que aconteceu', 
                    detailedMessage: 'detalhes do erro acontecido' 
                } 
            ]
        }
    }
    */

    IF cProp = "codAcoes" THEN DO:
        oFieldObj = NEW JsonObject().
        oFieldObj:add('label', 'Novo label').
        oFieldObj:add('required', TRUE).

        ASSIGN lFocus = TRUE
               cValue = "FocoDesIdioma".
        
        ASSIGN jObj = NEW JsonObject().
        jObj:add('code', '44').
        jObj:add('message', 'A UPC alterou algumas caracteristica da tela.'). 
        jObj:add('detailedMessage', 'Na execuçãoexecu‡Æo da UPC, houveram alteraçõesaltera‡äes nos campos de tela.').
        oMessages:add(jObj).
    END.
    
    /* Exemplo de JSON de retorno para o HTML      
    value: 'FocoDesIdioma',
    field: {
        label: 'Novo Label', 
        required: true 
    },
    focus: true,
    _messages: [ 
        { 
            code: '44', 
            message: 'A UPC alterou algumas caracteristica da tela.', 
            detailedMessage: 'Na execuçãoexecu‡Æo da UPC, houveram alteraçõesaltera‡äes nos campos de tela.' 
        } 
    ]
    */

    // atribui os valores de volta para a tela HTML
    jsonIO = NEW JSonObject().
    
    oReturn = NEW JSonObject().
    oReturn:add("value", cValue). // mantem o valor seta os valores dos campos ja ajustados
    oReturn:add("field", oFieldObj). // seta as propriedades dos campos a serem alteradas
    oReturn:add("focus", lFocus). // seta o focus a ser retornado para a tela html
    oReturn:add("_messages", oMessages). // seta as mensagens a serem retornadas para a tela html

    jsonIO:add("root", oReturn).
END PROCEDURE.

/* fim */

Resultado ao chamar ao API tendo uma UPC cadastrada:

Ao fazer as requisições, virão os seguintes resultados na UPC.

Bloco de código
titleResultado de leituras no backend Progress
linenumberstrue
collapsetrue
Busca do METADADOS onde foram adicionados os novos campos codUsuario, nomUsuario e codDialet:

POST - http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas/metadata

{PROCEDURE piI18N:
    DEFINE VARIABLE oParser      AS JsonAPIRequestParser NO-UNDO.
    "total": 9,
    "hasNext": false,
    "items": [
DEFINE VARIABLE oQueryParams AS JsonObject              {NO-UNDO.
    DEFINE VARIABLE pIdioma      "visible": true,
AS CHARACTER            "gridColumns": 6,NO-UNDO.
    
    ASSIGN 
   "disable": true,
    oParser      =  "property": "codIdioma",NEW JsonAPIRequestParser(jsonIO) 
        oQueryParams = oParser:GetQueryParams()
  "label": "Idioma",
     pIdioma      = oQueryParams:GetJsonArray("typelanguage": "string"):GetCharacter(1).
    
    },
IF (pIdioma = "pt-BR")     {THEN DO:
        jsonIO =   "visible": true,NEW JsonObject().
            "gridColumns": 6,jsonIO:Add("user", "Usu rio").
            "property": "desIdioma",jsonIO:Add("name", "Nome").
            "label": "DescriçãojsonIO:Add("regexTestValidation",
 "Teste Valida‡Æo REGEX").
         "type": "stringjsonIO:Add("cpfMaskApply",
 "Aplica‡Æo M scara CPF").
        jsonIO:Add("onlyNumbers", "required": true,Somente N£meros").
    END.
    ELSE IF (pIdioma = "validate": "/api/trn/v1/idiomas/validateField"en-US") THEN DO:
        },
jsonIO = NEW JsonObject().
     {
   jsonIO:Add("user", "User").
        jsonIO:Add("visiblename": true,, "Name").
            "gridColumns": 6,
   jsonIO:Add("regexTestValidation", "REGEX Test Validation").
         "property": "codIdiomPadrjsonIO:Add("cpfMaskApply",
 "CPF Apply Mask").
         "label":jsonIO:Add("onlyNumbers", "IdiomaOnly PadrãoNumbers",).
    END.
END PROCEDURE.

/* fim */

Resultado ao chamar ao API tendo uma UPC cadastrada:

Ao fazer as requisições, virão os seguintes resultados na UPC.

Bloco de código
titleResultado de leituras no backend Progress
linenumberstrue
collapsetrue
Busca do METADADOS onde foram "type": "string"
        },adicionados os novos campos codUsuario, nomUsuario e codDialet:

POST - http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas/metadata

{
    "total": 9,
    {"hasNext": false,
    "items": [
       "visible": true, {
            "gridColumnsvisible": 6true,
            "disablegridColumns": true6,
            "propertydisable": "datUltAtualiz"true,
            "formatproperty": "dd/MM/yyyycodIdioma",
            "label": "Última AtualizaçãoIdioma",
            "type": "datestring"
        },
        {
            "visible": true,
            "gridColumns": 6,
            "disableproperty": true"desIdioma",
            "propertylabel": "hraUltAtualizDescrição",
            "labeltype": "Hora Última Atualização""string",
            "required": true,
            "typevalidate": "string/api/trn/v1/idiomas/validateField"
        },
        {
            "visible": true,
            "gridColumns": false6,
            "property": "idcodIdiomPadr",
            "typelabel": "numberIdioma Padrão",
            "keytype": true"string"
        },
        {
            "visible": true,
            "gridColumns": 6,
            "disable": true,
            "property": "codUsuariodatUltAtualiz",
            "labelformat": "Usuáriodd/MM/yyyy",
            "typelabel": "stringÚltima Atualização",
            "requiredtype": true"date"
        },
        {
            "visible": true,
            "gridColumns": 6,
            "propertydisable": "nomUsuario"true,
            "labelproperty": "NomehraUltAtualiz",
            "typelabel": "stringHora Última Atualização",
            "requiredtype": true"string"
        },
        {
            "visible": truefalse,
            "gridColumnsproperty": 6"id",
            "propertytype": "codDialetnumber",
            "labelkey": "Dialeto",
true
        },
       "type": "string", {
            "requiredvisible": true,
        }
     ]
}

Busca dos dados onde foram adicionados novos valores:

GET - http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas

{
"gridColumns": 6,
            "totalproperty": 3,
    "hasNextcodUsuario": false,
    "items": [
        {"label": "Usuário",
            "codIdiomPadrtype": "99 Outrosstring",
            "codDialetrequired": "Pt",true
        },
    "codIdioma": "ale",    {
            "codUsuariovisible": "super"true,
            "desIdiomagridColumns": "Alemão"6,
            "hraUltAtualizproperty": "nomUsuario",
            "datUltAtualizlabel": null"Nome",
            "nomUsuariotype": "Superstring",
            "idrequired": 4580144true
        },
        {
            "codIdiomPadrvisible": "99 Outros"true,
            "codDialetgridColumns": "PT"6,
            "codIdiomaproperty": "ENcodDialet",
            "codUsuariolabel": "joaoDialeto",
            "desIdiomatype": "Inglesstring",
            "hraUltAtualizrequired": "",true
        }
    "datUltAtualiz": null,
            "nomUsuario": "Joao da Silva",
        ]
}

Busca dos dados onde foram adicionados novos valores:

GET - http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas

{
    "total": 3,
    "idhasNext": 194736false,
        },"items": [
        {
            "codIdiomPadr": "0399 EspanholOutros",
            "codDialet": "PTPt",
            "codIdioma": "ESale",
            "codUsuario": "Manoelsuper",
            "desIdioma": "EspanholAlemão",
            "hraUltAtualiz": "",
            "datUltAtualiz": null,
            "nomUsuario": "Manoel da SilvaSuper",
            "id": 29688984580144
        },
        ]
}

Front-End PO-UI

Introdução:

Para este exemplo vamos criar um CRUD com template dinâmico, onde serão mostrados os dados de acordo com o que o back-end retornar.

O desenvolvimento do frontend utilizando este campo componente se divide basicamente em três partes:

  • Routes:
    • Na definição da rota é onde vamos definir todos os caminhos dos componentes;
  • HTML
    • No HTML basta colocarmos os componentes, pois o metadados irá retornar o que precisamos para renderizar o componente;
  • TypeScript
    • No Typescript do componente vamos realizar uma pequena lógica para o tratamento dos dados de acordo com metadado;

Abaixo vamos mostrar como ficaram a parte de Listagem, Edição e Detalhe do nosso CRUD dinâmico.

Routes:

Abaixo segue exemplo de como ficará o arquivo de rotas de nossa aplicação CRUD.

Bloco de código
languagejs
titleapp-routing.module.ts
linenumberstrue
collapsetrue
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { IdiomaDetailComponent } from './idioma/detail/idioma-detail.component';
import { IdiomaEditComponent } from './idioma/edit/idioma-edit.component';
import { IdiomaListComponent } from './idioma/list/idioma-list.component';

const routes: Routes = [
  { path: 'idiomas/create', component: IdiomaEditComponent },
  { path: 'idiomas/edit/:id', component: IdiomaEditComponent },
  { path: 'idiomas/detail/:id', component: IdiomaDetailComponent },
  { path: 'idiomas', component: IdiomaListComponent },
  { path: '', redirectTo: '/idiomas', pathMatch: 'full' },
  { path: '**', component: IdiomaListComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Listagem:

É a tela inicial da nossa aplicação e mostra a lista de dados da tabela Idioma, onde foram adicionados através de customização três campos da tabela usuar_mestre. Esta tela dará acesso às outras funcionalidades como edição e detalhamento.

Bloco de código
languagexml
titleListagem - idioma-list.componente.html
linenumberstrue
collapsetrue
<po-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-dynamic-table
  p-auto-router
  [p-title]="cTitle"
  [p-actions]="actions"
  [p-breadcrumb]="breadcrumb"
  [p-fields]="fields"
  [p-service-api]="serviceApi">
</po-page-dynamic-table>
Bloco de código
languagejs
titleListagem - idioma-lista.component.ts
linenumberstrue
collapsetrue
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { PoBreadcrumb } from '@po-ui/ng-components';
import { PoPageDynamicTableActions } from '@po-ui/ng-templates';

import { IdiomaService } from './../resources/idioma.service';

@Component({
  selector: 'app-idioma-list',
  templateUrl: './idioma-list.component.html',
  styleUrls: ['./idioma-list.component.css']
})

export class IdiomaListComponent implements OnInit {
  // Definicao das variaveis utilizadas
  public cTitle = 'Manutenção de Idiomas';
  public serviceApi: string;
  public fields: Array<any> = [];
  public showLoading = false;

  public readonly actions: PoPageDynamicTableActions = {
    new: '/idiomas/create',
    detail: '/idiomas/detail/:id',
    edit: '/idiomas/edit/:id',
    remove: true,
    removeAll: true
  };

  public readonly breadcrumb: PoBreadcrumb = {
    items: [
      { label: 'Home', link: '/' },
      { label: 'Idiomas'}
    ]
  };

  // Construtor da classe
  constructor(
    private service: IdiomaService,
    private route: Router
  ) { }

  // Load do componente
  public ngOnInit(): void {
    this.fields = [];
    this.serviceApi = this.service.getUrl();
    this.showLoading = true;
    this.service.getMetadata().subscribe(resp => {
      this.fields = resp['items'];
      this.service.setFieldList(this.fields);
      this.showLoading = false;
    });
  }
}

Edição: 

{
            "codIdiomPadr": "99 Outros",
            "codDialet": "PT",
            "codIdioma": "EN",
            "codUsuario": "joao",
            "desIdioma": "Ingles",
            "hraUltAtualiz": "",
            "datUltAtualiz": null,
            "nomUsuario": "Joao da Silva",
            "id": 194736
        },
        {
            "codIdiomPadr": "03 Espanhol",
            "codDialet": "PT",
            "codIdioma": "ES",
            "codUsuario": "Manoel",
            "desIdioma": "Espanhol",
            "hraUltAtualiz": "",
            "datUltAtualiz": null,
            "nomUsuario": "Manoel da Silva",
            "id": 2968898
        }
    ]
}

Front-End PO-UI

Introdução:

Para este exemplo vamos criar um CRUD com template dinâmico, onde serão mostrados os dados de acordo com o que o back-end retornar.

O desenvolvimento do frontend utilizando este campo componente se divide basicamente em três partes:

  • Routes:
    • Na definição da rota é onde vamos definir todos os caminhos dos componentes;
  • HTML
    • No HTML basta colocarmos os componentes, pois o metadados irá retornar o que precisamos para renderizar o componente;
  • TypeScript
    • No Typescript do componente vamos realizar uma pequena lógica para o tratamento dos dados de acordo com metadado;


Abaixo vamos mostrar como ficaram a parte de Listagem, Edição e Detalhe do nosso CRUD dinâmico.

Routes:

Abaixo segue exemplo de como ficará o arquivo de rotas de nossa aplicação CRUD.

Bloco de código
languagejs
titleapp-routing.module.ts
linenumberstrue
collapsetrue
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { IdiomaDetailComponent } from './idioma/detail/idioma-detail.component';
import { IdiomaEditComponent } from './idioma/edit/idioma-edit.component';
import { IdiomaListComponent } from './idioma/list/idioma-list.component';

const routes: Routes = [
  { path: 'idiomas/create', component: IdiomaEditComponent },
  { path: 'idiomas/edit/:id', component: IdiomaEditComponent },
  { path: 'idiomas/detail/:id', component: IdiomaDetailComponent },
  { path: 'idiomas', component: IdiomaListComponent },
  { path: '', redirectTo: '/idiomas', pathMatch: 'full' },
  { path: '**', component: IdiomaListComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Listagem:

É a tela inicial da nossa aplicação e mostra a lista de dados da tabela Idioma, onde foram adicionados através de customização três campos da tabela usuar_mestre. Esta tela dará acesso às outras funcionalidades como edição e detalhamentoEsta tela permite a inclusão de um novo registro na tabela Idioma e também a alteração de registros já existentes.

Bloco de código
languagexml
titleEdição: Listagem - idioma-editlist.componentcomponente.html
linenumberstrue
collapsetrue
<po-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-editdynamic-table
  p-auto-router
  [p-title]="cTitle"
  [p-breadcrumbactions]="breadcrumbactions"
  [p-disable-submitbreadcrumb]="formEdit.form.invalidbreadcrumb"
  ([p-cancel)fields]="cancelClick()fields"
  ([p-service-save)api]="saveClick()serviceApi">
  <po</po-page-dynamic-form
    #formEdit
    p-auto-focus="string"
    [p-fields]="fields"
    p-validate="/api/trn/v1/idiomas/validateForm"
    [p-value]="record">
  </po-dynamic-form>
</po-page-edit>
table>
Bloco de código
languagejs
titleListagem - idioma-lista
Bloco de código
languagejs
titleEdição - idioma-edit.component.ts
linenumberstrue
collapsetrue
import { Component, OnInit, ViewChild } from '@angular/core';
import { NgFormRouter } from '@angular/formsrouter';
import { ActivatedRoute,PoBreadcrumb Router } from '@angular/router@po-ui/ng-components';
import { PoBreadcrumb, PoDialogService, PoNotificationServicePoPageDynamicTableActions } from '@po-ui/ng-componentstemplates';

import { IdiomaService } from './../resources/idioma.service';

@Component({
  selector: 'app-idioma-editlist',
  templateUrl: './idioma-editlist.component.html',
  styleUrls: ['./idioma-editlist.component.css']
})

export class IdiomaEditComponentIdiomaListComponent implements OnInit {
  // DefineDefinicao asdas variaveis a serem utilizadas
  public cTitle: string;
= 'Manutenção public currentId: stringde Idiomas';
  public record = {}serviceApi: string;
  public fields: Array<any> = [];
  public isUpdateshowLoading = false;

  public readonly actions: showLoadingPoPageDynamicTableActions = false;{

   public breadcrumbnew: PoBreadcrumb;

  // Obtem a referencia do componente HTML
  @ViewChild('formEdit', { static: true })
  formEdit: NgForm;

  // Construtor da classe com os servicos necessarios
  constructor(
    private service: IdiomaService,
    private activatedRoute: ActivatedRoute,
    private route: Router,'/idiomas/create',
    detail: '/idiomas/detail/:id',
    edit: '/idiomas/edit/:id',
    remove: true,
    removeAll: true
  };

  public readonly breadcrumb: PoBreadcrumb = {
    items: [
      { label: 'Home', link: '/' },
      { label: 'Idiomas'}
    ]
  };

  // Construtor da classe
  constructor(
    private poDialogservice: PoDialogServiceIdiomaService,
    private poNotificationroute: PoNotificationServiceRouter
  ) { }

  // Load do componente
  public ngOnInit(): void {
    this.isUpdatefields = false[];
    this.showLoadingserviceApi = true;
this.service.getUrl();
    // Carrega o registro pelo IDthis.showLoading = true;
    this.activatedRouteservice.paramsgetMetadata().subscribe(parsresp => {
      this.currentIdfields = parsresp['iditems'];

      // Se nao tiver o ID definido sera um CREATEthis.service.setFieldList(this.fields);
      this.showLoading = false;
    });
  }
}

Edição: 

Esta tela permite a inclusão de um novo registro na tabela Idioma e também a alteração de registros já existentes.

Bloco de código
languagexml
titleEdição: idioma-edit.component.html
linenumberstrue
collapsetrue
<po-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-edit
  [p-title]="cTitle"
  [p-breadcrumb]="breadcrumb"
  [p-disable-submit]="formEdit.form.invalid"
  (p-cancel)="cancelClick()"
  (p-save)="saveClick()">
  <po-dynamic-form
    #formEdit
    p-auto-focus="string"
    [p-fields]="fields"
    p-validate="/api/trn/v1/idiomas/validateForm"
    [p-value]="record">
  </po-dynamic-form>
</po-page-edit>
Bloco de código
languagejs
titleEdição - idioma-edit.component.ts
linenumberstrue
collapsetrue
import { Component, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { PoBreadcrumb, PoDialogService, PoNotificationService } from '@po-ui/ng-components';

import { IdiomaService } from './../resources/idioma.service';

@Component({
  selector: 'app-idioma-edit',
  templateUrl: './idioma-edit.component.html',
  styleUrls: ['./idioma-edit.component.css']
})

export class IdiomaEditComponent implements OnInit {
  // Define as variaveis a serem utilizadas
  public cTitle: string;
  public currentId: string;
  public record = {};
  public fields: Array<any> = [];
  public isUpdate = false;
  public showLoading = false;

  public breadcrumb: PoBreadcrumb;

  // Obtem a referencia do componente HTML
  @ViewChild('formEdit', { static: true })
  formEdit: NgForm;

  // Construtor da classe com os servicos necessarios
  constructor(
    private service: IdiomaService,
    private activatedRoute: ActivatedRoute,
    private route: Router,
    private poDialog: PoDialogService,
    private poNotification: PoNotificationService
  ) { }

  // Load do componente
  public ngOnInit(): void {
    this.isUpdate = false;
    this.showLoading = true;

    // Carrega o registro pelo ID
    this.activatedRoute.params.subscribe(pars => {
      this.currentId = pars['id'];

      // Se nao tiver o ID definido sera um CREATE
      if (this.currentId === undefined)  if (this.currentId === undefined) {
        this.isUpdate = false;
        this.cTitle = 'Inclusão de Idioma';
      } else {
        this.isUpdate = true;
        this.cTitle = 'Alteração de Idioma';
      }

      // Atualiza o breadcrumb de acordo com o tipo de edicao
      this.breadcrumb = {
        items: [
          { label: 'Home', action: this.beforeRedirect.bind(this) },
          { label: 'Idiomas', action: this.beforeRedirect.bind(this) },
          { label: this.cTitle }
        ]
      };

      // Se for uma alteracao, busca o registro a ser alterado
      if (this.isUpdate) {
        this.service.getById(this.currentId).subscribe(resp => {
          Object.keys(resp).forEach((key) => this.record[key] = resp[key]);

          // Em alteracao temos que receber o registro para depois buscar a lista de campos
          this.getMetadata();
        });
      } else {
        // Se for create, pega a lista de camposthis.isUpdate = false;
        this.cTitle = 'Inclusão de Idioma';
      } else {
        this.getMetadata()isUpdate = true;
       }
 this.cTitle =  })'Alteração de Idioma';
      }

      // RetornaAtualiza ao listabreadcrumb de campos
acordo com o privatetipo getMetadata()de {edicao
    let fieldList: Array<any>this.breadcrumb = [];{

    // Carrega a lista de campos, trabalhando com um cache da lista de campos
    fieldList = this.service.getFieldList(this.isUpdate);items: [
          { label: 'Home', action: this.beforeRedirect.bind(this) },
    if (fieldList === null || fieldList.length === 0) {
 label: 'Idiomas', action:   this.servicebeforeRedirect.getMetadatabind().subscribe(resp => {
this) },
          { label: this.service.setFieldList(resp['items']);
cTitle }
        ]
    this.fields = this.service.getFieldList(this.isUpdate); };

      // Se this.showLoadingfor = false;
      });
uma alteracao, busca o registro a ser alterado
      } elseif (this.isUpdate) {
        this.service.getById(this.fieldscurrentId).subscribe(resp => fieldList;{
      this.showLoading    Object.keys(resp).forEach((key) => this.record[key] = falseresp[key]);

      }
  }

  // Em Redirecionaalteracao viatemos breadcrumb
que receber private beforeRedirect(itemBreadcrumbLabel) {
    if (this.formEdit.valid) {o registro para depois buscar a lista de campos
          this.route.navigate(['/']getMetadata();
        });
 else {
    } else this.poDialog.confirm({
        title:// `Confirma o redirecionamento para ${itemBreadcrumbLabel}`,
  Se for create, pega a lista de campos
      message: `Existem dados que não foram salvos ainda. Você tem certeza que quer sair ?`,
        confirm: () => this.route.navigate(['/'])
      });
    }
  }

  // Grava o registro quando clicado no botao Salvar
  public saveClick(): void {
    this.showLoading = true;
    if (this.isUpdate) {
      // Altera um registro ja existente this.getMetadata();
      }
    });
  }

  // Retorna a lista de campos
  private getMetadata() {
    let fieldList: Array<any> = [];

    // Carrega a lista de campos, trabalhando com um cache da lista de campos
    fieldList = this.service.getFieldList(this.isUpdate);
    if (fieldList === null || fieldList.length === 0) {
      this.service.update(this.currentId, this.recordgetMetadata().subscribe(resp => {
        this.poNotificationservice.successsetFieldList('Idioma alterado com sucesso'resp['items']);
        this.showLoadingfields = falsethis.service.getFieldList(this.isUpdate);
        this.route.navigate(['/idiomas'])showLoading = false;
      });
    } else {
      // Cria um registro novothis.fields = fieldList;
      this.service.create(this.record).subscribe(respshowLoading => {false;
    }
  }

  this.poNotification.success('Idioma criado com sucesso');
   // Redireciona via breadcrumb
  private beforeRedirect(itemBreadcrumbLabel) {
    if (this.formEdit.showLoading = false;valid) {
        this.route.navigate(['/idiomas']);
      });
    }
  }

else {
  // Cancela a edicao e redireciona ao clicar no botao Cancelar
  public cancelClick(): void {
    this.poDialog.confirm({
        title: 'Confirma cancelamento',
 `Confirma o redirecionamento para ${itemBreadcrumbLabel}`,
        message: 'Existem`Existem dados que não foram salvos ainda. Você tem certeza que quer cancelarsair ?'`,
        confirm: () => this.route.navigate(['/'])
      });
    }
}

Detalhe:

Esta tela apresenta os detalhes de um registro de Idioma, com suas customizações.

Bloco de código
languagexml
titleDetalhe: idioma-detail.component.html
linenumberstrue
collapsetrue
<po-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-detail
  [p-title]="cTitle"
  [p-breadcrumb]="breadcrumb"
  (p-edit)="editClick()"
  (p-back)="goBackClick()">
  <po-dynamic-view 
    [p-fields]="fields"
    [p-value]="record">
  </po-dynamic-view>
</po-page-detail>
Bloco de código
languagejs
titleDetalhe: idioma-detail.component.ts
linenumberstrue
collapsetrue
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { PoBreadcrumb } from '@po-ui/ng-components';

import { IdiomaService } from './../resources/idioma.service';

@Component({
  selector: 'app-idioma-detail',
  templateUrl: './idioma-detail.component.html',
  styleUrls: ['./idioma-detail.component.css']
})

export class IdiomaDetailComponent implements OnInit {
  // definicao das variaveis utilizadas
  public cTitle = 'Detalhe do Idioma';
  public currentId: string;
  public fields: Array<any> = [];
  public record = {};
  public   }

  // Grava o registro quando clicado no botao Salvar
  public saveClick(): void {
    this.showLoading = true;
    if (this.isUpdate) {
      // Altera um registro ja existente
      this.service.update(this.currentId, this.record).subscribe(resp => {
        this.poNotification.success('Idioma alterado com sucesso');
        this.showLoading = false;
        this.route.navigate(['/idiomas']);
      });
    } else {
      // Cria um registro novo
      this.service.create(this.record).subscribe(resp => {
        this.poNotification.success('Idioma criado com sucesso');
        this.showLoading = false;

  public readonly breadcrumb: PoBreadcrumb = { items: [ this.route.navigate(['/idiomas']);
      });
    }
  }

  // Cancela a edicao e redireciona ao clicar no botao Cancelar
  public cancelClick(): void {
    this.poDialog.confirm({
      title: 'Confirma cancelamento',
      message: 'Existem dados que não foram salvos ainda. Você tem certeza que quer cancelar ?',
      confirm: () => this.route.navigate(['/'])
    });
  }
}

Detalhe:

Esta tela apresenta os detalhes de um registro de Idioma, com suas customizações.

Bloco de código
languagexml
titleDetalhe: idioma-detail.component.html
linenumberstrue
collapsetrue
<po-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-detail
  [p-title]="cTitle"
  [p-breadcrumb]="breadcrumb"
  (p-edit)="editClick()"
  (p-back)="goBackClick()">
  <po-dynamic-view 
    [p-fields]="fields"
    [p-value]="record">
  </po-dynamic-view>
</po-page-detail>
Bloco de código
languagejs
titleDetalhe: idioma-detail.component.ts
linenumberstrue
collapsetrue
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { PoBreadcrumb } from '@po-ui/ng-components';

import { IdiomaService } from './../resources/idioma.service';

@Component({
  selector: 'app-idioma-detail',
  templateUrl: './idioma-detail.component.html',
  styleUrls: ['./idioma-detail.component.css']
})

export class IdiomaDetailComponent implements OnInit {
  // definicao das variaveis utilizadas
  public cTitle = 'Detalhe do Idioma';
  public currentId: string;
  public fields: Array<any> = [];
  public record = {};
  public showLoading = false;

  public readonly breadcrumb: PoBreadcrumb = { items: [
      { label: 'Home', link: '/' },
      { label: 'Idiomas', link: '/idiomas' },
      { label: 'Detail' } ]
  };

  // construtor com os servicos necessarios
  constructor(
    private service: IdiomaService,
    private activatedRoute: ActivatedRoute,
    private route: Router
  ) {{ label: 'Home', link: '/' },
      { label: 'Idiomas', link: '/idiomas' },
      { label: 'Detail' } ]
  };

  // construtor com os servicos necessarios
  constructor(
    private service: IdiomaService,
    private activatedRoute: ActivatedRoute,
    private route: Router
  ) { }

  // load do componente
  public ngOnInit(): void {
    this.activatedRoute.params.subscribe(pars => {
      this.showLoading = true;

      // carrega o registro pelo ID
      this.currentId = pars['id'];
      this.service.getById(this.currentId).subscribe(resp => {
        Object.keys(resp).forEach((key) => this.record[key] = resp[key]);

        // carrega a lista de campos somente apos receber o registro a ser apresentado
        this.fields = this.service.getFieldList(false);
        if (this.fields === null || this.fields.length === 0) {
          this.service.getMetadata().subscribe(data => {
            this.fields = data['items'];
            this.service.setFieldList(this.fields);
            this.showLoading = false;
          });
        }
        this.showLoading = false;
      });
    });
  }

  // Redireciona quando clicar no botao Edit
  public editClick(): void {
    this.route.navigate(['/idiomas', 'edit', this.currentId]);
  }

  // Redirecionaload quando clicar no botao Voltardo componente
  public goBackClickngOnInit(): void {
    this.activatedRoute.routeparams.navigate(['/idiomas']);
  }
}

06. VALIDAÇÃO DE COMPONENTES

Para os itens a seguir, são apresentados algumas formas de interação com os componentes presentes na interface, bem como possíveis validações sobre os mesmos. 

Esconder ou visualizar os campos

Como a geração da tela dinâmica é automática, para esconder determinados campos basta setar o atributo visible para FALSE na montagem do JsonObject de retorno do metadados. 

Bloco de código
languagejs
titleEsconder campos na interface
linenumberstrue
collapsetrue
... 
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'codDialet').
    jObj:add('label', 'Dialeto').
    jObj:add('visible', FALSE). // <- Remove o item da tela de todos seus correspondentes (Form, View, Table)
    jObj:add('required', TRUE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    jObj:add('gridColumns', 6).
    jObj:add('validate', '/api/trn/v1/idiomas/validateField').
    jAList:add(jObj).
...

Validação de componentes na interface

Uma boa prática em desenvolvimento de telas é a validação de alguns campos na própria interface, cujo intuito é reduzir requisições desnecessárias ao 'Back-End'. As funcionalidades apresentadas a seguir podem ser utilizadas conjunto com a validação do próprio Form ([p-disable-submit]="formEdit.form.invalid"), no qual pode desabilitar o botão de confirmação enquanto houver campos inválidos.

Utilização do pattern (RegEx)

Para a validação de campos textos, pode ser utilizado o atributo pattern qualquer expressão regular, caso não atenda ao RegEx, uma mensagem de erro definida em errorMessage é apresentada em tela. 

Bloco de código
languagejs
titleUtilização de pattern
linenumberstrue
collapsetrue
... 
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'testeValidacaoRegEx').
    jObj:add('label', 'Teste Validação RegEx').
    jObj:add('gridColumns', 6).
    jObj:add('pattern', "[0-9]~{2~}"). // <- Validacao RegEx
    jObj:add('errorMessage', 'Obrigatório mínimo 2 números consecutivos.').
    jAList:add(jObj).
...

Utilização de limites em numeração

subscribe(pars => {
      this.showLoading = true;

      // carrega o registro pelo ID
      this.currentId = pars['id'];
      this.service.getById(this.currentId).subscribe(resp => {
        Object.keys(resp).forEach((key) => this.record[key] = resp[key]);

        // carrega a lista de campos somente apos receber o registro a ser apresentado
        this.fields = this.service.getFieldList(false);
        if (this.fields === null || this.fields.length === 0) {
          this.service.getMetadata().subscribe(data => {
            this.fields = data['items'];
            this.service.setFieldList(this.fields);
            this.showLoading = false;
          });
        }
        this.showLoading = false;
      });
    });
  }

  // Redireciona quando clicar no botao Edit
  public editClick(): void {
    this.route.navigate(['/idiomas', 'edit', this.currentId]);
  }

  // Redireciona quando clicar no botao Voltar
  public goBackClick(): void {
    this.route.navigate(['/idiomas']);
  }
}

06. VALIDAÇÃO DE COMPONENTES

Para os itens a seguir, são apresentados algumas formas de interação com os componentes presentes na interface, bem como possíveis validações sobre os mesmos. 

Esconder ou visualizar os campos

Como a geração da tela dinâmica é automática, para esconder determinados campos basta setar o atributo visible para FALSE na montagem do JsonObject de retorno do metadados. Com a utilização dos atributos minValue e maxValue, é possível efetuar a restrição de períodos da numeração que pode ser utilizado em conjunto com o type para restringir a digitação em somente números. Caso houver números inválidos, a mensagem definida em errorMessage é apresentada na tela.

Bloco de código
languagejs
titleUtilização intervalo de valoresEsconder campos na interface
linenumberstrue
collapsetrue
... 
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'numberValidatecodDialet').
    jObj:add('label', 'Somente númerosDialeto').
    jObj:add('visible', TRUEFALSE).
 // <-  jObj:add('required', FALSE).
    jObj:add('minValue', 1).
    jObj:add('maxValue', 9).Remove o item da tela de todos seus correspondentes (Form, View, Table)
    jObj:add('errorMessagerequired', 'Somente números de 1 a 9'). // <- Mensagem de erro 1-9TRUE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('integercharacter')). // <- Restringe a digitacao somente numeros
    jObj:add('gridColumns', 6).
    jAListjObj:add(jObj'validate', '/api/trn/v1/idiomas/validateField').
    jAList:add(jObj).
...

Utilização de máscaras para os campos

Validação de componentes na interface

Uma boa prática em desenvolvimento de telas é a validação de alguns campos na própria interface, cujo intuito é reduzir requisições desnecessárias ao 'Back-End'. As funcionalidades apresentadas a seguir podem ser utilizadas conjunto com a validação do próprio Form ([p-disable-submit]="formEdit.form.invalid"), no qual pode desabilitar o botão de confirmação enquanto houver campos inválidos.

Utilização do pattern (RegEx)

Para a validação de campos textos, pode ser utilizado o atributo pattern qualquer expressão regular, caso não atenda ao RegEx, uma mensagem de erro definida em errorMessage é apresentada em tela. Quando é definida uma máscara mask, ocorre a restrição de digitação no próprio campo. Para o exemplo abaixo, é permitido digitar somente números e ao efetuar a digitação, a máscara será aplicada automaticamente.

Bloco de código
languagejs
titleUtilização de máscaraspattern
linenumberstrue
collapsetrue
...   
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'numberRangeValidatetesteValidacaoRegEx').
    jObj:add('label', 'AplicaçãoTeste deValidação máscara CPFRegEx').
    jObj:add('maskgridColumns', '999.999.999-99'6).
    jObj:add('pattern', "[0-9]~{2~}"). // <-- MascaraValidacao CPFRegEx
    jObj:add('visibleerrorMessage', TRUE 'Obrigatório mínimo 2 números consecutivos.').
    jAList:add(jObj).
...

Utilização de limites em numeração

Com a utilização dos atributos minValue e maxValue, é possível efetuar a restrição de períodos da numeração que pode ser utilizado em conjunto com o type para restringir a digitação em somente números. Caso houver números inválidos, a mensagem definida em errorMessage é apresentada na tela.

Bloco de código
languagejs
titleUtilização intervalo de valores
linenumberstrue
collapsetrue
... 
    ASSIGN jObj = NEW JsonObject().
    jObj:add('requiredproperty', FALSE'numberValidate').
    jObj:add('typelabel', JsonAPIUtils:convertAblTypeToHtmlType('character'Somente números')).
    jObj:add('gridColumnsvisible', 6TRUE).
    jAListjObj:add(jObj).
...

Validações no back-end

O po-dynamic-form permite dois tipos de validações, a validação do formulário completo ou por campo.

A validação do formulário completo vamos detalhar mais a frente.

A validação por campo é feito através da validação campo-a-campo, onde você conseguirá alterar algumas características do campo que esta sendo validado.

Aviso

Nas validações de campos é possível somente alterar as características do próprio campo validado, onde não é permitido alterar nas características de outros campos.

Bloco de código
languagejs
titleUtilização de validação de campo
linenumberstrue
collapsetrue
...   
    ASSIGN jObj = NEW JsonObject('required', FALSE).
    jObj:add('minValue', 1).
    jObj:add('propertymaxValue', 'nomCampo'9).
    jObj:add('labelerrorMessage', 'Label do campoSomente números de 1 a 9').
 // <- Mensagem  jObj:add('visible', TRUE).de erro 1-9
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('characterinteger')).
 // <-  jObj:add('validate', '/api/trn/v1/idiomas/validateField'Restringe a digitacao somente numeros
    jObj:add('gridColumns', 6).
    jAList:add(jObj).
...

A adição da tag validate na definição do campo, onde deverá ser especificado a URL de validação, fará com que sempre que o campo for alterado pelo usuário, será enviado uma requisição para o back-end para realizar a validação desse campo.

JSon que recebemos da tela HTML

O componente PO-DYNAMIC-FORM, quando ocorre alguma alteração em seus campos, no LEAVE do campo, ele enviará um JSon com o seguinte formato para o back-end:

...

Utilização de máscaras para os campos

Quando é definida uma máscara mask, ocorre a restrição de digitação no próprio campo. Para o exemplo abaixo, é permitido digitar somente números e ao efetuar a digitação, a máscara será aplicada automaticamente.

Onde o back-end receberá o seguinte JSon:

Bloco de código
languagejs
titleJSON enviado para o back-end de validação do campoUtilização de máscaras
linenumberstrue
collapsetrue
{
...   
     "property": "campoValidado",ASSIGN jObj = NEW JsonObject().
    "value": "valorAtualDoCampoValidado"
}

JSon que retornamos para a tela HTML

A validação do campo, aguarda o seguinte formato de JSon:

...

jObj:add('property', 'numberRangeValidate').
    jObj:add('label', 'Aplicação de máscara CPF').
    jObj:add('mask', '999.999.999-99').  // <-- Mascara CPF
    jObj:add('visible', TRUE).
    jObj:add('required', FALSE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    jObj:add('gridColumns', 6).
    jAList:add(jObj).
...

Validações no back-end

O po-dynamic-form permite dois tipos de validações, a validação do formulário completo ou por campo.

A validação do formulário completo vamos detalhar mais a frente.

A validação por campo é feito através da validação campo-a-campo, onde você conseguirá alterar algumas características do campo que esta sendo validado.

Aviso

Nas validações de campos é possível somente alterar as características do próprio campo validado, onde não é permitido alterar nas características de outros campos.

Bloco de código
languagejs
titleUtilização de validação de campo
linenumberstrue
collapsetrue
...   
    ASSIGN jObj = NEW JsonObject().
    jObj:add('property', 'nomCampo').
    jObj:add('label', 'Label do campo').
    jObj:add('visible', TRUE).
    jObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    jObj:add('validate', '/api/trn/v1/idiomas/validateField'
    jObj:add('gridColumns', 6).
    jAList:add(jObj).
...


A adição da tag validate na definição do campo, onde deverá ser especificado a URL de validação, fará com que sempre que o campo for alterado pelo usuário, será enviado uma requisição para o back-end para realizar a validação desse campo.

JSon que recebemos da tela HTML

O componente PO-DYNAMIC-FORM, quando ocorre alguma alteração em seus campos, no LEAVE do campo, ele enviará um JSon com o seguinte formato para o back-end

O back-end, após processar e realizar a validação necessária, retornará para o front-end o seguinte JSon:

Bloco de código
languagejs
titleJSON de retorno do back-end da validação de campo
linenumberstrue
collapsetrue
{
    value: 'novoValorDoCampoValidado',
    field: {
        mask: '99.999.999/9999-99',
        required: true 
    },
    focus: true,
    _messages: [
        {
            code: '01', 
            message: 'Mensagem do erro que aconteceu', 
            detailedMessage: 'detalhes do erro acontecido' 
        }
    ]
}

Para utilizarmos a UPC de customização, tivemos que encapsular o JSon recebido no back-end, dentro da procedure pValidateField, e tambem o JSon a ser enviado para a tela HTML, conforme o exemplo abaixo:

Bloco de código
languagejs
titleJSon empacsulado para UPC de Validação de Campos
linenumberstrue
collapsetrue
// encapsulamos o retorno para enviar para a UPC
oObj = NEW JsonObject().
oObj:add("property", cProp).
oObj:add("root", oRet).

// Realiza a chamada da UPC Progress
{include/i-epcrest.i &endpoint=validateField &event=validateField &jsonVar=oObj}    

// obtem o retorno customizado, onde o mesmo foi alterado e retornado na tag root 
oRet = oObj:getJsonObject("root").

Neste nosso exemplo, nós dividimos o JSon a ser enviado para UPC em três partes, que são:

TagTipoDescrição
propertyCharacterComtêm Contêm o nome do campo que esta sendo houve a alteração para ser validado.
rootvalueJSonObjectCharacterContêm um JSonObject com que será retornado para o HTML e que poderá ser customizado na UPC. Tudo que for customizado deverá estar dentro desta tag.

Exemplo de JSon recebido pela UPC:

o valor atual do campo para ser validado.


Onde o back-end receberá o seguinte JSon:

Bloco de código
languagejs
titleJSON enviado para o back-end de validação do campo
Bloco de código
titleJSon recebido na UPC para Validação do Campo
linenumberstrue
collapsetrue
{ 
    "property": 'nomeDoCampoValidado'"campoValidado",
    "value": "valorAtualDoCampoValidado"
}


JSon que retornamos para a tela HTML

A validação do campo, aguarda o seguinte formato de JSon:

TagTipoDescrição
valueCharacterContêm o novo valor para o campo.
fieldJSonObjectContêm uma lista de propriedades que serão alteradas. OBS: Estas propriedades tem efeito somente sobre o campo que está sendo validado.
focusLogicalInforma se o campo validado deverá ou não receber o focus.
_messagesJSonArrayContêm uma lista de mensagens "de erro" que podem ser apresentadas ao voltar para o HTML.


O back-end, após processar e realizar a validação necessária, retornará para o front-end o seguinte JSon:

Bloco de código
languagejs
titleJSON de retorno do back-end da validação de campo
linenumberstrue
collapsetrue
{
    value: 'novoValorDoCampoValidadoroot: {
        value: '',
        field: {
            mask: '99.999.999/9999-99',
 
       required: },true 
    },
    focus: falsetrue,
        _messages: [ 
            { 
                code: '01', 
                message: 'Mensagem do erro que aconteceu', 
                detailedMessage: 'detalhes do erro acontecido' 
            } 
       ]
    }
}


Após Para utilizarmos a customização pela UPC, sera devolvido para a API Rest apenas a tag root, com as customizações necessárias, conforme o exemplo abaixo onde temos o resultado de uma customização:UPC de customização, tivemos que encapsular o JSon recebido no back-end, dentro da procedure pValidateField, e tambem o JSon a ser enviado para a tela HTML, conforme o exemplo abaixo:

Bloco de código
languagejs
titleJSon empacsulado para UPC de Validação de Campos
Bloco de código
titleJSon retornado pela UPC para Validações
linenumberstrue
collapsetrue
{
// encapsulamos o retorno para enviar para a UPC
oObj = NEW JsonObject().
oObj:add("property", cProp).
oObj:add("root", oRet).

// Realiza a chamada da UPC Progress
{include/i-epcrest.i &endpoint=validateField &event=validateField &jsonVar=oObj}    

// obtem o retorno customizado, onde o mesmo foi alterado e retornado na tag root 
oRet = oObj:getJsonObject("root").


Neste nosso exemplo, nós dividimos o JSon a ser enviado para UPC em três partes, que são:

TagTipoDescrição
propertyCharacterComtêm o nome do campo que esta sendo validado.
rootJSonObjectContêm um JSonObject com que será retornado para o HTML e que poderá ser customizado na UPC. Tudo que for customizado deverá estar dentro desta tag.


Exemplo de JSon recebido pela UPC:

Bloco de código
titleJSon recebido na UPC para Validação do Campo
linenumberstrue
collapsetrue
{ 
    property: 'nomeDoCampoValidado',
    root: {
value: '',
    field: {
        mask: '99.999.999/9999-99',
       label: 'Novo Label' 
    },
    focus: false,
    _messages: [ 
        { 
            codevalue: '01', 
        field: {
            mask: '99.999.999/9999-99' 
        },
        focus: false,
        _messages: [ 
            { 
                code: '01', 
                message: 'Mensagem do erro que aconteceu', 
                detailedMessage: 'detalhes do erro acontecido' 
            } 
       ]
    }

07. VALIDAÇÃO DE FORMULÁRIOS

O quê deve ser alterado no componente PO-DYNAMIC-FORM

Para validarmos um formulário, temos que configurar primeiro o nosso componente po-dynamic-form para ficar apto a enviar as ocorrências de validações. Para isso temos que especificar no componente po-dynamic-form a tag p-validate, onde informamos a URL que fará a validação do form, neste exemplo será "/api/trn/v1/idiomas/validateForm".


}


Após a customização pela UPC, sera devolvido para a API Rest apenas a tag root, com as customizações necessárias, conforme o exemplo abaixo onde temos o resultado de uma customização:

Bloco de código
titleJSon retornado pela UPC para Validações
Bloco de código
languagejs
titleTag p-validate no po-dynamic-form
linenumberstrue
collapsetrue
{
  <po-dynamic-form  value: '',
    field: #formEdit{
     p-auto-focus="string"   mask: '99.999.999/9999-99',
       label: 'Novo Label' 
    [p-fields]="fields"
},
     p-validate="/api/trn/v1/idiomas/validateForm"focus: false,
    [p-value]="record">
  </po-dynamic-form>
Aviso

As validações de formulário validam somente os campos já existentes, onde não é permitido adicionar novos campos. Para adicionar novos campos deve-se utilizar a tag p-load, que é executada logo após a inicialização do componente.

JSon que recebemos da tela HTML

_messages: [ 
        { 
            code: '01', 
            message: 'Mensagem do erro que aconteceu', 
            detailedMessage: 'detalhes do erro acontecido' 
        } 
   ]
}

07. VALIDAÇÃO DE FORMULÁRIOS

O quê deve ser alterado no componente PO-DYNAMIC-FORM

Para validarmos um formulário, temos que configurar primeiro o nosso componente po-dynamic-form para ficar apto a enviar as ocorrências de validações. Para isso temos que especificar no componente po-dynamic-form a tag p-validate, onde informamos a URL que fará a validação do form, neste exemplo será "/api/trn/v1/idiomas/validateForm".

Bloco de código
languagejs
titleTag p-validate no po-dynamic-form
linenumberstrue
collapsetrue
  <po-dynamic-form
    #formEdit
    p-auto-focus="string"
    [p-fields]="fields"
    p-validate="/api/trn/v1/idiomas/validateForm"
    [p-value]="record">
  </po-dynamic-form>
Aviso

As validações de formulário validam somente os campos já existentes, onde não é permitido adicionar novos campos. Para adicionar novos campos deve-se utilizar a tag p-load, que é executada logo após a inicialização do componente.


JSon que recebemos da tela HTML

O componente PO-O componente PO-DYNAMIC-FORM, quando ocorre alguma alteração em seus campos, no LEAVE do campo, ele enviará um JSon com o seguinte formato para o back-end:

...

Bloco de código
languagejs
titleJSon de retorno do backend para o HTML
linenumberstrue
collapsetrue
{    
    value:     
    value: {
      desIdioma: 'teste de escrita',
      hraUltAtualiz: '17:18:19'
    },
    fields: [
      {
        property: 'codCpfCnpj', 
        mask: '99.999.999/9999-99' 
      }
    ],
    focus: 'hraUltAtualiz',
    _messages: [ 
        { 
            code: '01', 
            message: 'Mensagem do erro que aconteceu', 
            detailedMessage: 'detalhes do erro acontecido' 
        } 
    ]
 }


Para utilizarmos a UPC de customização, tivemos que encapsular o JSon recebido no back-end, dentro da procedure pValidateForm, e tambem o JSon a ser enviado para a tela HTML, conforme o exemplo abaixo:

Bloco de código
languagejs
titleJSon empacsulado para UPC de Validação de Formulários
linenumberstrue
collapsetrue
// encapsulamos o retorno para enviar para a UPC
oObj = NEW JsonObject().
oObj:add("property", cProp).
oObj:add("originalValues", oValue).
oObj:add("root", oRet).

// Realiza a chamada da UPC Progress
{include/i-epcrest.i &endpoint=validateForm &event=validateForm &jsonVar=oObj}    

// obtem o retorno customizado, onde o mesmo foi alterado e retornado na tag root 
oRet = oObj:getJsonObject("root").


Neste nosso exemplo, nós dividimos o JSon a ser enviado para UPC em três partes, que são:

TagTipoDescrição
propertyCharacterComtêm o nome do campo que esta sendo validado.
originalValuesJsonObjectContêm um JSonObject com propriedades contendo os nomes dos campos e os seus respectivos valores.
rootJSonObjectContêm um JSonObject com que será retornado para o HTML e que poderá ser customizado na UPC. Tudo que for customizado deverá estar dentro desta tag.


Exemplo de JSon recebido pela UPC:

Bloco de código
titleJSon recebido na UPC para Validação do Formulário
linenumberstrue
collapsetrue
{ 
    property: 'codAcao',
    originalValues: {
        "codIdiomPadr": "01 Português",
        "codIdioma": "12345678",
        "desIdioma": "12345678901234567890",
        "hraUltAtualiz": "",
        "datUltAtualiz": null,
        "id": 6,
        "codAcoes": "FocoDesIdioma"
    },
    root: {
        value: {
            desIdioma: 'teste de escrita',
            hraUltAtualiz: '17:18:19'
        },
        fields: [
            {
                property: 'codCpfCnpj', 
                mask: '99.999.999/9999-99' 
            }
        ],
        focus: 'hraUltAtualiz',
        _messages: [ 
            { 
                code: '01', 
                message: 'Mensagem do erro que aconteceu', 
                detailedMessage: 'detalhes do erro acontecido' 
            } 
        ]
    }
}


Após a customização pela UPC, sera devolvido para a API Rest apenas a tag root, com as customizações necessárias, conforme o exemplo abaixo onde temos o resultado de uma customização:

Bloco de código
titleJSon retornado pela UPC para Validações
linenumberstrue
collapsetrue
{
    "_messages": [
        {
            "detailedMessage": "Na execução da UPC, houveram alterações nos campos de tela.",
            "code": "44",
            "message": "A UPC alterou algumas caracteristica da tela."
        }
    ],
    "focus": "desIdioma",
    "fields": [
        {
            "property": "codIdiomPadr",
            "disabled": true
        }
    ],
    "value": {
        "desIdioma": "Valor customizado na UPC"
    }
}

08. FACILITADORES PROGRESS

Criamos facilitadores para auxiliar no desenvolvimento das API's, ficam localizados na classe Progress "com.totvs.framework.api.JsonAPIUtils":

MétodoDescriçãoAssinatura/Exemplo
convertAblTypeToHtmlTypeConverte os tipos nativos do Progress para os tipos esperados pelo PO-UI

Assinatura:

convertAblTypeToHtmlType (INPUT cType AS CHARACTER)

Exemplo:

ASSIGN cType = JsonAPIUtils:convertAblTypeToHtmlType ("integer").

O retorno no cType será "number", que é um formato reconhecido pelo PO-UI.

convertToCamelCaseConverter os nomes dos campos lidos da tabela, normalmente com "_", para "camel case", que é o mais comum utilizado em Json's.   

Assinatura:

convertToCamelCase (INPUT cKey AS CHARACTER)

Exemplo:

ASSIGN cField= JsonAPIUtils:convertToCamelCase ("cod_e_mail_usuar").

O retorno no cField será "codEMailUsuar", que é o campo em Camel Case.

getIdFieldRetorna um campo do tipo ID para ser adicionado na lista de campos do Metadata. Este campo serve como chave do registro nos tratamentos de CRUD na parte HTML.

Assinatura:

getIdField()

Exemplo:

oIdiomas:add( JsonAPIUtils:getIdField() ).

09. TÉCNICAS PARA TRADUÇÃO

breve descricao 

Bloco de código
...
    ASSIGN oObj = NEW JsonObject().
    oObj:add('property', 'codIdioma').
    oObj:add('label', "~{~{language~}~}").
    oObj:add('visible', TRUE).
    oObj:add('disable', TRUE).
    oObj:add('type', JsonAPIUtils:convertAblTypeToHtmlType('character')).
    oObj:add('gridColumns', 6).
    oIdiomas:add(oObj).
...


No Front-end, deve-se efetuar a substitução para que as literais 

Bloco de código
...
public getFieldList(update, literals) {
    // ajusta alista de campos para habilitar ou nao a chave primaria se for CREATE
    let fields: Array<any> = [];
    if (this.fieldList.length > 0) {
      this.fieldList.forEach((data) => {
      desIdioma: 'teste de escrita',
   if (data['label'] !== undefined) {
   hraUltAtualiz: '17:18:19'
    },
  const key fields:= data['label'].replace('{{', '').replace('}}', '');
      {
    if (literals[key] !==  property: 'codCpfCnpj', 
undefined) {
           mask: '99.999.999/9999-99' data['label'] = literals[key];
      }
    ],}
    focus: 'hraUltAtualiz',
   }
 _messages: [ 
     if (data['options'] !== undefined) { 
          let options code:= data['01options', ];
            message: 'Mensagem do erro que aconteceu', options.forEach((option) => {
            detailedMessage:const 'detalheskey do erro acontecido' = option['label'].replace('{{', '').replace('}}', '');
        } 
   if (literals[key] !== undefined) {
 }

Para utilizarmos a UPC de customização, tivemos que encapsular o JSon recebido no back-end, dentro da procedure pValidateForm, e tambem o JSon a ser enviado para a tela HTML, conforme o exemplo abaixo:

Bloco de código
languagejs
titleJSon empacsulado para UPC de Validação de Formulários
linenumberstrue
collapsetrue
// encapsulamos o retorno para enviar para a UPC
oObj = NEW JsonObject().
oObj:add("property", cProp).
oObj:add("originalValues", oValue).
oObj:add("root", oRet).

// Realiza a chamada da UPC Progress
{include/i-epcrest.i &endpoint=validateForm &event=validateForm &jsonVar=oObj}    

// obtem o retorno customizado, onde o mesmo foi alterado e retornado na tag root 
oRet = oObj:getJsonObject("root").

Neste nosso exemplo, nós dividimos o JSon a ser enviado para UPC em três partes, que são:

...

              option['label'] = literals[key];
            }
          });
        }
        fields.push(data);
      });
    }
    return fields;
}
...


Exemplo de chamadas

Bloco de código
...
    // carrega a lista de campos somente apos receber o registro a ser apresentado
    this.fields = this.service.getFieldList(false, this.literals);
    if (this.fields === null || this.fields.length === 0) {
      this.service.getMetadata().subscribe(data => {

Exemplo de JSon recebido pela UPC:

Bloco de código
titleJSon recebido na UPC para Validação do Formulário
linenumberstrue
collapsetrue
{ 
    property: 'codAcao',
    originalValues: {
        "codIdiomPadr": "01 Português",
        "codIdioma": "12345678",
        "desIdioma": "12345678901234567890",
        "hraUltAtualiz": "",
        "datUltAtualiz": null,
        "id": 6,this.fields = data['items'];
        "codAcoes": "FocoDesIdioma"
this.service.setFieldList(this.fields);
     },
   this.showLoading root:= {false;
      });
  value: {
         }
...


Avaisjdioasjdi aisodjsiad 

Bloco de código
...

const i18nConfig: PoI18nConfig = {
  default: {
     desIdiomalanguage: 'teste de escritapt-BR',
            hraUltAtualizcontext: '17:18:19general',
    cache:  false
  },
  contexts: {
     fieldsgeneral: [{
            {'pt-BR': generalPt,
      'en-US': generalEn,
         propertyurl: 'codCpfCnpj', api/trn/v1/idiomas/translations'
                mask: '99.999.999/9999-99' }
  }
};

...


Definição das literais no back-end

Bloco de código
/**
    Recupera as literais
*/
PROCEDURE pIdiomas:
    DEFINE }
INPUT  PARAMETER oJsonInput  AS JsonObject  ],
  NO-UNDO.
    DEFINE OUTPUT focus: 'hraUltAtualiz',
  PARAMETER oJsonOutput AS JsonObject    NO-UNDO.
  _messages: [ 
    // Realiza a chamada da UPC Progress
  { 
     {include/i-epcrest.i &endpoint=i18n &event=i18n &jsonVar=oJsonInput}
    
    ASSIGN oJsonOutput = oJsonInput.
END PROCEDURE.



Bloco de código
...
IF  pEndPoint = "i18n"
AND pEventcode: '01', 
         = "i18n" THEN DO ON STOP UNDO, messageLEAVE:
 'Mensagem do erro que aconteceu', RUN piI18N.
END.

...

PROCEDURE piI18N:
    DEFINE VARIABLE oParser      AS JsonAPIRequestParser   detailedMessage: 'detalhes do erro acontecido' 
  NO-UNDO.
    DEFINE VARIABLE oQueryParams AS JsonObject          } NO-UNDO.
    DEFINE VARIABLE pIdioma   ]
    }
}

Após a customização pela UPC, sera devolvido para a API Rest apenas a tag root, com as customizações necessárias, conforme o exemplo abaixo onde temos o resultado de uma customização:

Bloco de código
titleJSon retornado pela UPC para Validações
linenumberstrue
collapsetrue
{
AS CHARACTER           "_messages": [ NO-UNDO.
    
    {ASSIGN 
        oParser    "detailedMessage": "Na execução da UPC, houveram alterações nos campos de tela.",
  = NEW JsonAPIRequestParser(jsonIO) 
        oQueryParams = oParser:GetQueryParams()
        pIdioma      = oQueryParams:GetJsonArray("codelanguage": "44",):GetCharacter(1).
    
    IF (pIdioma  = "message": "A UPC alterou algumas caracteristica da tela."pt-BR") THEN DO:
        jsonIO = NEW JsonObject().
        }jsonIO:Add("user", "Usu rio").
    ],
    jsonIO:Add("focusname":, "desIdiomaNome",).
       "fields": [ jsonIO:Add("regexTestValidation", "Teste Valida‡Æo REGEX").
        {
    jsonIO:Add("cpfMaskApply", "Aplica‡Æo M scara CPF").
        jsonIO:Add("propertyonlyNumbers":, "codIdiomPadr",Somente N£meros").
    END.
    ELSE IF (pIdioma = "disabled": trueen-US") THEN DO:
        }
jsonIO =   ],
    "value": {NEW JsonObject().
        jsonIO:Add("desIdiomauser":, "Valor customizado na UPC"
    }
}

07. FACILITADORES PROGRESS

Criamos facilitadores para auxiliar no desenvolvimento das API's, ficam localizados na classe Progress "com.totvs.framework.api.JsonAPIUtils":

...

Assinatura:

convertAblTypeToHtmlType (INPUT cType AS CHARACTER)

Exemplo:

ASSIGN cType = JsonAPIUtils:convertAblTypeToHtmlType ("integer").

O retorno no cType será "number", que é um formato reconhecido pelo PO-UI.

...

Assinatura:

convertToCamelCase (INPUT cKey AS CHARACTER)

Exemplo:

ASSIGN cField= JsonAPIUtils:convertToCamelCase ("cod_e_mail_usuar").

O retorno no cField será "codEMailUsuar", que é o campo em Camel Case.

...

Assinatura:

getIdField()

Exemplo:

oIdiomas:add( JsonAPIUtils:getIdField() ).

"User").
        jsonIO:Add("name", "Name").
        jsonIO:Add("regexTestValidation", "REGEX Test Validation").
        jsonIO:Add("cpfMaskApply", "CPF Apply Mask").
        jsonIO:Add("onlyNumbers", "Only Numbers").
    END.
END PROCEDURE.




10

...

LINKS ÚTEIS

Documentação API Datasul:

...

...

11CONCLUSÃO

A ideia era apresentar uma técnica para que possibilita-se as áreas de negócio, de forma segura e simples, disponibilizarem pontos de customização em suas API’s REST.

...