Complete los datos en un Observable basado en datos en el Observable mismo

Nov 28 2020

En primer lugar: soy nuevo en Angular, por lo que los términos aquí pueden estar equivocados. Espero poder entenderlo.

Estoy consumiendo una API. El punto final A expone Tickets; estos Tickets contienen una matriz (0- *) de Attachments. Esos Attachments no están completos; en el modelo de datos que contienen la cadena de base 64 del contenido del archivo subyacente pero para reducir la carga general de los Attachments son sólo se completa cuando se tomaron de punto final B. Sin embargo, con el fin de ir a buscar una Attachmentde las de punto final de BI necesitan Attachment's idque sólo puedo conseguir desde el punto final A.

Lo que necesito que suceda es que obtengo attachment.iddel Endpoint A, uso esa identificación para buscar el approriate Attachmentdel Endpoint B, y reemplace Attachments en el resultado del Endpoint A con el resultado del Endpoint B. Sin embargo, dado que los Attachments se recuperan como Observablepuedo ' Simplemente asigne el valor.

El siguiente fragmento es uno de los varios intentos de resolver esto y puede obtener los Attachmentmensajes de correo electrónico de la API, pero no están asignados de la manera que quería.

ngOnInit(): void {
  console.log(`getting ticket for id ${this.id}`); this.ticket$ = this.ticketService.getTicket(this.id).pipe(
    map((response:Ticket) => {
      response.attachments.map((attachmentPartly:Attachment) => {
        console.log(`getting attachment for id ${attachmentPartly.id}`); let attachmentFilled = this.ticketService.getAttachment(attachmentPartly.id); attachmentFilled.subscribe(att => console.log(`base64 content is: ${att.base64Content}`)); //for posterity: this line has been omitted in tests with no change in result
        return attachmentFilled;
      });
      return response;
    })
  );
}

getTicketdevuelve un Observable<Ticket>. getAttachmentdevuelve un Observable<Attachment>.

Como se puede ver, comencé a depurar registrando (dado que la aplicación angular está alojada dentro de un sistema más grande que también proporciona autorización contra los puntos finales, realmente no puedo depurarla de la manera correcta ...) y el valor base64 está escrito a la consola, por lo que al menos algunas partes del código funcionan como quiero. Por ejemplo, para un archivo de texto / sin formato, puedo obtener el valor "SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw" escrito en la consola que coincide con el valor encontrado cuando ejecuto la consulta manualmente a través de Postman.

Sospecho que estoy tratando de asignar un Observable( attachmentFilled) al Attachment[]pero TS no advierte sobre conversiones de tipos ilegales. Al mirar la pestaña Red en Chrome DevTools, no puedo ver la llamada getAttachmentssolo a las llamadas a getTickets. Los modelos de datos para Tickety Attachmentson:

export interface Ticket {
    id?:               string;
    description?:      string;
    assignee?:         string;
    createdAt?:        string;
    resolvedAt?:       string;
    attachments?:      Attachment[];
}

export interface Attachment {
    name?:          string; //contained in result from Endpoint A
    id?:            string; //contained in result from Endpoint A
    relatesTo?:     string; 
    type?:          string; //contained in result from Endpoint A
    sizeBytes?:     string; //contained in result from Endpoint A
    hash?:          string;
    url?:           string;
    base64Content?: string; //need to fetch this
}

Estoy usando la siguiente plantilla para crear los enlaces del archivo de descarga (muy inspirado por el archivo base64 de descarga de angular-file-saver usando FileSaver ):

<div *ngFor="let attachment of ticket.attachments" class="list-group-item">
    <div class="d-flex w-100 justify-content-between">
        <a [href]="'data:' + attachment.type + ';base64,' + attachment.base64Content | safeUrl" target="_blank" download="{{ attachment.name }}">{{ attachment.name }}</a>
        <small>{{ attachment.sizeBytes | filesize }}</small>
    </div>
    <small class="mb-1">{{ attachment.type }}</small>
</div>

que da como resultado el siguiente html (observe cómo solo se completan los datos del punto final A):

<div _ngcontent-dmp-c28="" class="list-group-item">
    <div _ngcontent-dmp-c28="" class="d-flex w-100 justify-content-between">
        <a _ngcontent-dmp-c28="" target="_blank" download="test.txt" href="data:text/plain;base64,undefined">test.txt</a>
        <small _ngcontent-dmp-c28="">43 B</small>
    </div>
    <small _ngcontent-dmp-c28="" class="mb-1">text/plain</small>
</div>

(La desinfección de URL, inspirada en el valor de URL inseguro de desinfección de Angular2 Base64 , parece forzar el contenido nulo de base64 a undefined. Antes de la desinfección, no habría nada donde ahora hay undefined).

El getAttachmentpunto final solo acepta attachment.idcomo entrada.

He intentado implementar la solución en Transform data de una manera observable de esta manera:

ngOnInit(): void {
    console.log(`getting ticket for id ${this.id}`); this.ticket$ = this.ticketService.getTicket(this.id).pipe(
    mergeMap((response: Ticket) => forkJoin([...response.attachments.map((attachment : Attachment) => this.ticketService.getAttachment(attachment.id))])));
}

Pero para no esperar: this.ticket$arroja un error "El tipo 'Observable <Attachment []>' no se puede asignar al tipo 'Observable'. El tipo 'Attachment []' no tiene propiedades en común con el tipo 'Ticket'"

Respuestas

1 MoxxiManagarm Nov 28 2020 at 12:25

Creo que es un problema clásico que se plantea aquí muy a menudo.

ngOnInit(): void {
  this.ticket$ = this.ticketService.getTicket(this.id).pipe( switchMap((response: Ticket) => { const filledAttechments$ = response.attachments.map(
        (attachmentPartly: Attachment) => this.ticketService.getAttachment(attachmentPartly.id).pipe(
          map(att => ({ ...attachmentPartly, base64Content: att.base64Content })),
        ),
      );

      return forkJoin(filledAttechments$).pipe(
        map(filledAttachments => ({...response, attachments: filledAttachments})),
      );
    })
  );
}