استخدام OpenReplay مع Angular
من المهم أن تضع في اعتبارك أن التتبّع يجب أن يعمل خارج خطافات Zone.js الخاصة بـ Angular لتجنّب التداخل والفحوصات غير الضرورية.
فيما يلي مثال بسيط على إعداد الـ tracker:
import { Injectable, NgZone } from '@angular/core';
import Tracker from '@openreplay/tracker';
import trackerAssist from '@openreplay/tracker-assist';
@Injectable({
providedIn: 'root',
})
export class OpenReplayService {
public tracker?: Tracker | null;
constructor(private zone: NgZone) {
this.zone.runOutsideAngular(() => {
this.tracker = new Tracker({
projectKey: 'abc123',
ingestPoint: 'https://someurl/',
});
this.tracker.use(
trackerAssist({
confirmText: `You have an incoming call from <Company> Support. Do you want to answer?`,
})
);
});
}
public async start() {
this.zone.runOutsideAngular(() => {
if (this.tracker) {
return this.tracker.start();
} else {
return {
sessionID: null,
sessionToken: null,
userUUID: null,
};
}
});
}
public setUserData(user: { id: string }): void {
this.zone.runOutsideAngular(() => {
if (this.tracker && user.id) {
this.tracker.setUserID(String(user.id));
}
});
}
}
تتبّع طلبات HTTP
Section titled تتبّع طلبات HTTPإذا كنت تحاول تتبّع الطلبات المُرسَلة عبر تطبيق Angular الخاص بك، فإن OpenReplay لا يوفّر إضافة (plugin) كما يفعل مع Fetch أو Axios.
ومع ذلك، لا يزال بإمكانك تهيئته لتتبّع طلباتك بالمعلومات التي تريدها باستخدام HTTPInterceptor.
في هذا الدليل التعليمي سأوضّح لك كيفية إنشاء HTTPInterceptor قادر على تسجيل كل من الطلبات والاستجابات المُرسَلة من قِبل HTTPClient الخاص بك.
إنشاء المُعترِض (interceptor)
Section titled إنشاء المُعترِض (interceptor)المُعترِض هو نوع خاص من الكائنات يمكنك حقنه في كود تطبيقك لالتقاط كل طلب على حدة يُرسَل عبر عميل HTTP الافتراضي في Angular والتقاط الاستجابة.
من خلال هذا المنطق، سنستفيد من الأحداث المخصّصة في OpenReplay، والتي تتيح لك إرسال أي حدث ترغب في التقاطه داخل جلستك، لذا سنحاكي ما تفعله إضافة Fetch أو Axios في الإعدادات الأخرى.
الكود الخاص بالمُعترِض هو كما يلي:
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpResponse,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { ReplaySessionService } from '../replay-session.service';
@Injectable({providedIn: 'root'})
export class HttpConfigInterceptor implements HttpInterceptor {
constructor(
private replaySessionService: ReplaySessionService,
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
//This function will be called with the response a few lines below
const handleResponse = (request: HttpRequest<any>, response: HttpResponse<any>, event: string) => {
//we forward our data to the service, which will create the custom event and send it
this.replaySessionService.sendEventToReplaySession(event, { request, response })
}
return next.handle(request).pipe(
//filter out events that aren't http reponses
filter( (event: any) => event instanceof HttpResponse),
map( (resp: HttpResponse<any>) => { //for each response, call handleResponse
handleResponse(request, resp, `${request.url}`)
return resp
}),
map((event: HttpEvent<any>) => {
return event;
})
);
}
}
سننظر في خدمة الـ replay بعد لحظة، ولكن افترض الآن أنها موجودة فحسب. احفظ هذا الملف داخل مجلد app الخاص بك.
ثم عدّل ملف app.module لإضافة ما يلي داخل توجيه @ngModule:
providers: [
{provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true}
]
وبالتالي ينبغي أن يبدو ملف app.module على نحوٍ شبيه بهذا:
import { NgModule } from '@angular/core';
/*
imports...
*/
import { HttpConfigInterceptor } from './interceptor/index';
@NgModule({
imports: [
/*...*/
],
providers: [
{provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true}
],
declarations: [
/*...*/
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
بعد الانتهاء من هذا، أصبح تطبيقك الآن يعرف أنه يجب حقن المُعترِض الخاص بك في كل طلب HTTP يتم تنفيذه.
والآن لنلقِ نظرة على خدمة session replay الفعلية.
إنشاء SessionReplayService
Section titled إنشاء SessionReplayServiceأولاً، أضِف خدمتك الجديدة باستخدام الأمر التالي:
$ ng generate service replay-session
سيؤدي هذا إلى إنشاء خدمة Angular جديدة في جذر تطبيقك باسم replay-session.service.ts
ينبغي أن يبدو محتوى ذلك الملف على هذا النحو:
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpResponse,
} from '@angular/common/http';
import OpenReplay from '@openreplay/tracker'
type ReqRespType = {
request: HttpRequest<any>,
response: HttpResponse<any>
}
@Injectable({
providedIn: 'root'
})
export class ReplaySessionService {
tracker: OpenReplay|null = null
constructor() {
this.tracker = new OpenReplay({
projectKey: "<YOUR PROJECT KEY>",
})
//you can set up any other OR plugins here as well
this.tracker.start()
}
sendEventToReplaySession(event: string, params: ReqRespType): void {
const {request, response} = params
this.tracker?.event(event + "[request]", {
method: request.method,
url: request.url,
params: request.params
})
this.tracker?.event(event + "[response]", {
body: response.body,
status: response.status,
headers: response.headers
})
}
}
إن مُنشئ الصنف (constructor)، والذي نعلم أنه لن يُستدعى إلا مرة واحدة لأن هذا خدمة، يتولّى إنشاء نسخة من الـ tracker وبدء تشغيله.
ثم في طريقتنا sendEventToReplaySession، نستخدم الطريقة event لإرسال حدثين مخصّصين.
إذا عدت إلى صنف المُعترِض، فستلاحظ أن الـ “event” (المعامل الأول الذي نحصل عليه في هذه الطريقة) هو في الواقع عنوان URL، لذا أُلحِق به الكلمتين “[response]” و”[request]” لتحديد ما يُسجَّل وأين. ثم أُنشئ الحمولات (payloads) لكل حدث، مع حفظ المعلومات التي أرغب في حفظها فقط.
في الواقع، يمكنك حتى أخذ ذلك الكود وتنقية أي حقل من الطلب أو الاستجابة لا تريد أن يكون مرئيًا داخل الـ replay قبل استدعاء الطريقة event.
يمكنك الاطّلاع على هذا المستودع للحصول على الكود المصدري الكامل لتطبيق يعمل قائم على Angular يستخدم الـ Tracker.
هل لديك أسئلة؟
Section titled هل لديك أسئلة؟إذا واجهت أي مشكلات في إعداد الـ Tracker في مشروع Angular الخاص بك، يرجى التواصل معنا عبر مجتمعنا على Slack واطرح أسئلتك على مطوّرينا مباشرةً!