Chicos y chicas aprovechad esta oportunidad para certificarse. Paso 1: REGISTRARSE en el VS2020

Hay tiempo de preparar el examen ya que se puede realizar online en cualquier momento hasta el 31 de Diciembre.

¿Qué os parece si hacemos una serie de webinars explicando los contenidos como preparación para el examen?

Glup, eso no me lo esperaba!!... tendría que mirar desde cuándo está el JSON Adaptor

Pero para tu problema eso va a dar igual, me explico. El parámetro "jsonObject" es el que utiliza esa clase %JSON.Adaptor pero esa clase lo único que hace es ayudarte a manejar objetos JSON. Para resolver tu problema inicial en lugar de usar esa clase utiliza como tu dices un Stream. Por ejemplo el %Stream.GlobalCharacter.

Esta sentencia:

set tSC=..PostJSON(tURL,.tHttpResponse,pRequest.orden)

Lo que hace es pasar la URL, el objeto donde se va a colocar la respuesta a la vuelta (por referencia) y el contenido del mensaje pRequest. En este caso puedes inventarte un objeto tuyo que lleve un Stream con un JSON en texto plano para probar.

En este método:

Method PostJSON(pURL As %String, Output pHttpResponse As %Net.HttpResponse, pStream As %Stream.GlobalCharacter) As %Status
{
Return ..SendHTTP("POST",pURL,.pHttpResponse,pStream)
}

cambia la firma del método para usar el Stream. Y finalmente en este método envía el contenido como Body de la llamada HTTP.

Method SendHTTP(pHttpVerb As %String, pURL As %String, Output pHttpResponse As %Net.HttpResponse, pStream As %Stream.GlobalCharacter) As %Status
{
set httpRequest = ##class(%Net.HttpRequest).%New()     set httpRequest.ContentType = "application/json"     if $IsObject(jsonObject) {           set tSC=httpRequest.EntityBody.CopyFrom(pStream)   if $$$ISERR(tSC)   quit tSC     } set tSC = ..Adapter.SendFormDataURL(pURL,.pHttpResponse,pHttpVerb,httpRequest)     quit tSC }

No lo he probado. Creo que el uso del método CopyFrom es correcto pero si te falla es fácil copiar el contenido de un Stream a otro mediante Write y Read.

Espero que te sirva. 

Para empezar veo que estás creando una clase Adaptador que hereda de Test.BO.Adapter.HTTPOutboundAdapter. Eso no es una Operación. Es un adaptador.

Y la verdad no veo dónde estás metiendo los datos del Body... quizá hay más código

Yo mejor haría una operación que herede de EnsLib.REST.Operation.

Ejemplo:

Class demo.bo.RESTOperation Extends EnsLib.REST.Operation
{

Parameter INVOCATION = "Queue";

Method SendHTTP(pHttpVerb As %String, pURL As %String, Output pHttpResponse As %Net.HttpResponse, jsonObject As %JSON.Adaptor) As %Status
{
    set httpRequest = ##class(%Net.HttpRequest).%New()
    set httpRequest.ContentType = "application/json"
    if $IsObject(jsonObject) {
        set tSC=jsonObject.%JSONExportToStream(httpRequest.EntityBody)
        if $$$ISERR(tSC) quit tSC
    }  
    set tSC = ..Adapter.SendFormDataURL(pURL,.pHttpResponse,pHttpVerb,httpRequest)
    quit tSC
}

Method PostJSON(pURL As %String, Output pHttpResponse As %Net.HttpResponse, jsonObject As %JSON.Adaptor) As %Status
{
    Return ..SendHTTP("POST",pURL,.pHttpResponse,jsonObject)
} 

Method OnPostRequest(pRequest As demo.PostRequest, Output pResponse As demo.PostResponse) As %Status
{
    set tURL=..Adapter.URL_"/demo"
    set tSC=..PostJSON(tURL,.tHttpResponse,pRequest.orden)
    if $$$ISERR(tSC)&&$IsObject(tHttpResponse)&&$IsObject(tHttpResponse.Data)&&tHttpResponse.Data.Size {
        set tSC=$$$ERROR($$$EnsErrGeneral,$$$StatusDisplayString(tSC)_":"_tHttpResponse.Data.Read())
    }
    if $$$ISERR(tSC) quit tSC
    // procesar respuesta
    set pResponse = ##class(demo.msg.PostResponse).%New()
    set pResponse.respuesta = ##class(demo.msg.Respuesta).%New()
    if tHttpResponse.StatusCode=200 {
        $$$TRACE(tHttpResponse.Data.Read())
        set tSC = pResponse.respuesta.%JSONImport(tHttpResponse.Data)
    } else { 
        set tSC=$$$ERROR($$$EnsErrGeneral,"HTTP Status:"_tHttpResponse.StatusCode)
    }
    quit tSC
}



XData MessageMap
{
<MapItems>
    <MapItem MessageType="demo.PostRequest"> 
        <Method>OnPostRequest</Method>
    </MapItem>
</MapItems>
}

}

Puede que me equivoque, pero estoy viendo un 500 Internal Server Error, eso es un error que te devuelve el servicio. Por lo tanto ni siquiera se producen los mensajes. ¿Puedes elevar el nivel de log o meter trazas para saber qué está pasando? Por otro lado veo que el ContentType es "text/plain" y debería ser "application/json"

Para ver los flags de compilación disponibles:

USER>Do $system.OBJ.ShowFlags()

 See $system.OBJ.ShowQualifiers() for comprehensive list of qualifiers as flags have been superseded by qualifiers

    b - Include sub classes.
    c - Compile. Compile the class definition(s) after loading.
    d - Display. This flag is set by default.
    e - Delete extent.
    h - Show hidden classes.
    i - Validate XML export format against schema on Load.
    k - Keep source.  When this flag is set, source code of
        generated routines will be kept.
    l - Lock classes while compiling.  This flag is set by default.
    p - Percent.  Include classes with names of the form %*.
    r - Recursive.  Compile all the classes that are dependency predecessors.
    s - Process system messages or application messages.
    u - Update only.  Skip compilation of classes that are already up-to-date.
    y - Include classes that are related to the current class in the way that
        they either reference to or are referenced by the current class in SQL usage.

These flags are deprecated a, f, g, n, o, q, v
Default flags for this namespace 
You may change the default flags with the SetFlags(flags,system) classmethod.

Hola,

$SYSTEM.OBJ.CompileAllNamespaces() -- Compila las clases de TODOS los namespaces

Normalmente es más sencillo ir Namespace por Namespace (si hay varios) y se hace con $SYSTEM.OBJ.CompileAll(). Este método puede recibir "flags" de compilación. Yo suelo usar "ckub":

$SYSTEM.OBJ.CompileAll("ckub")

Hola Mathew,

Siempre se puede hacer una clase que herede de %Persistent y que exponga los métodos que quieras para guardar por ejemplo. Internamente allí puedes crear tu Callback y luego llamar al %Save.

Algo así como:

Class MiPersistente Extends %Persistent {

Method OnGuardar() {
}

Method Guardar() {
  ..OnGuardar()
  ..%Save()
}
}

Luego en lugar de heredar de %Persistent heredas de esta clase y sobre escribes el Callback.

No lo he probado pero es una alternativa a investigar

Gracias

Kurro dices que tu clase hereda de FTPService, si ves en el error la línea donde te da el error ¿es código tuyo o es código de la clase FTPService?

Enhorabuena a los ganadores!! 

Como ya habéis visto vamos a hacer concurso cada mes, espero que os animéis a participar !!!

Cualquier duda sobre todo esto preguntad en la comunidad... estamos abiertos 24x7 ;-)

Lo que @Kurro Lopez te ha presentado con detalle es lo que te decía en la primera respuesta:

También podría estar todo junto si no hay solapamiento entre nombres de paquetes y clases.

Como ellos ya lo han hecho han elegido la forma que mejor les ha funcionado. Por lo que desde luego es una buena solución.

El uso de mapeo de paquetes es util para no tener que duplicar clases (y código) en diferentes Namespaces. Pero es un mecanismo asociado a la operación/despliegue pero no al desarrollo. Son cuestiones diferentes. Puedo desarrollar un paquete de forma común y luego repartirlo entre varios namespaces (como una librería) o puedo ponerlo en un lugar accesible por varios (aka mapeo). Pero lo haga de una manera o de otra no me afecta a como lo desarrollo.

Sí claro, eso tiene mucho sentido. Lo único es que os toca gestionar varios Repos. Pero será muy práctico ir identificando funcionalidad común y llevarlo  al Repo común. Luego las clases comunes gestionarlos como librerías independientes. Si el esquema que tenéis se despliega todo en la misma instancia entonces también podéis hacer mapeos de clases entre Namespaces.

Hola Jorge, 

Me surgen varias dudas. Si trabajáis con diferentes Namespaces es porque son proyectos diferentes o ¿cómo están relacionados?. En principio lo lógico es que cada Namespace disponga de un Repo diferente. También podría estar todo junto si no hay solapamiento entre nombres de paquetes y clases. Ya que la idea es que cada desarrollador tenga un entorno aislado y por lo tanto el que toque clases de un Namespace normalmente no tocará las otras y esas no molestan. Sin embargo es muy importante que los despliegues en el servidor en producción se hagan de manera controlada y mediante paquetes de despliegue que se construyan ex-profeso para el mismo. No se si me he explicado bien...

Pero en resumen, una cosa es como se organiza el código en el desarrollo y otra diferente es como repartas las clases en el despliegue

Solo para dar alguna idea de que se podría enviar al concurso, echa un ojo a Rosetta Code - hay un montón de oportunidades de implementar esto u otros algoritmos populares que ya están implementados en otros lenguajes pero no en ObjectScript.

Hola,

Yo te diría que no se dispone de esa característica directamente pero sin embargo es posible implementar algo similar de una manera muy eficiente mediante procedimiento almacenado en Object Script. Si envías un poco más de detalle del objetivo especifico podemos revisar y enviar algún ejemplo.

Gracias

Hola,

Gracias por preguntar. 

Por lo que veo me parece que esto es un mensaje de MONMGR. Si es así este mensaje es generado por el Core y no por la para de Interoperabilidad así que ahora mismo el mecanismo es solo correo. Pero se me ocurre que pueden utilizar un servicio de lectura de correo y generar una alerta (Ens.Alert) que puedan manipular a su criterio. Aunque claro siendo una advertencia del correo es posible que haya procesos que se paren.