Implementing ETag in Angular 2

Implementing the odata standard which utilizes ETag has presented a challenge for me, particularly with PATCH requests. Each PATCH request requires sending the ETag in the header as If-None-Match. A HTTP status of 200 indicates that the change was successfully applied, while a status of 412 signifies that the data on the server has been modified and needs to be fetched again for merging (which is beyond the scope of this question).

I've managed to create a working solution so far, where adding data and Etag to cache is done in the implementation of the get() method:

export const HEADER_ETAG_MATCH = 'If-None-Match';
export const ODATA_ETAG_PROPERTY = '@odata.etag';
export interface CacheRecordStructure {
    etag: string;
    response: Response;
}
export class CachingService {
    cache: { [key: string]: CacheRecordStructure } = {};
    constructor(private http: Http) { }
    patch(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
        let stream$ = new Subject<Response>();
        this.http[type](url, body, this.addEtagHeader(url, options)).subscribe(response => {
            if (response.status === 412) {
                delete this.cache[url];
                this.get(url, options).subscribe(response2 => {
                    response2.status = 412;
                    stream$.next(response2);
                });
            } else {
                this.cache[url].etag = response.json()[ODATA_ETAG_PROPERTY];
                this.cache[url].response = response;
                stream$.next(response);
            }
        });
        return stream$.asObservable();
    }
}

Question 1: How can I alter this code to use Rx-only without having to define stream$?

Question 2: I would like to throw an error instead of returning status 412. Is it possible to include the newly fetched object from the server along with this error?

Answer №1

To achieve a minimally invasive solution, one approach could involve implementing the use of switchMap. Here is an example implementation:

export const HEADER_ETAG_MATCH = 'If-None-Match';
export const ODATA_ETAG_PROPERTY = '@odata.etag';
export interface CacheRecordStructure {
    etag: string;
    response: Response;
}
export class CachingService {
    cache: { [key: string]: CacheRecordStructure } = {};
    constructor(private http: Http) { }
    patch(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
        return this.http[type](url, body, this.addEtagHeader(url, options))
            .switchMap(response => {
                if (response.status === 412) {
                    delete this.cache[url];
                    return this.get(url, options)
                        .switchMap(response2 => {
                            response2.status = 412;
                            return Observable.throw(response2);
                        });
                } else {
                    this.cache[url].etag = response.json()[ODATA_ETAG_PROPERTY];
                    this.cache[url].response = response;
                    return Observable.of(response);
                }
            });
    }
}

Finally, you can utilize it like this:

myCachingService.patch("myurl...", someBody)
    .subscribe(
        response => console.log(response),
        errorWithNewObj => console.error(errorWithNewObj),
        () => console.info("Done!")
    );

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the latest whatwg-fetch update with TypeScript version 2.5.3

Within my TypeScript project, I am currently utilizing "whatwg-fetch": "2.0.3" as the latest version of this polyfill. Additionally, for types, I am using version "@types/whatwg-fetch": "0.0.33", and everything functions smoothly when working with TypeScri ...

Identify and handle errors effectively using TypeScript

I have a question regarding my Express server setup. Here is the code snippet: import express from "express"; import helmet from "helmet"; import cors from "cors"; const app = express(); app.use(helmet()); app.use(cors()); a ...

Form for creating and updating users with a variety of input options, powered by Angular 2+

As I work on creating a form, I encounter the need to distinguish between two scenarios. If the user selects 'create a user', the password inputs should be displayed. On the other hand, if the user chooses to edit a user, then the password inputs ...

"Enhancing User Experience with Hover States in Nested React Menus

I have created a nested menu in the following code. My goal is to dynamically add a selected class name to the Nav.Item element when hovering, and remove it only when another Nav.Item is hovered over. I was able to achieve this using the onMouseOver event. ...

What is a superior option to converting to a promise?

Imagine I am creating a function like the one below: async function foo(axe: Axe): Promise<Sword> { // ... } This function is designed to be utilized in this manner: async function bar() { // acquire an axe somehow ... const sword = await foo ...

Avoid the sudden change in page content when using Router.Navigate

When the link below is clicked, the current page jumps to the top before proceeding to the next page. <a href="javascript:void(0);" (click)="goToTicket(x.refNo, $event)">{{x.ticketTitle}}</a> component.ts goToTicket(refNo, e) { e.prev ...

I am having trouble getting debugging with source maps to work in VSCode and Browserify

I'm currently experiencing an issue with setting breakpoints in Chrome using VSCode, especially when working with TypeScript and Browserify. Oddly enough, if I open Chrome separately and manually refresh the page, the source maps load correctly and I ...

The border of the Material UI Toggle Button is not appearing

There seems to be an issue with the left border not appearing in the toggle bar below that I created using MuiToggleButton. Any idea what could be causing this? Thank you in advance. view image here view image here Just a note: it works correctly in the ...

Error TS2322: Cannot assign type 'Promise<Hero | undefined>' to type 'Promise<Hero>'

I am currently studying angular4 using the angular tutorial. Here is a function to retrieve a hero from a service: @Injectable() export class HeroService { getHeroes(): Promise<Hero[]> { return new Promise(resolve => { // ...

What is the best way to simulate a constructor-created class instance in jest?

Suppose there is a class called Person which creates an instance of another class named Logger. How can we ensure that the method of Logger is being called when an instance of Person is created, as shown in the example below? // Logger.ts export default cl ...

Determine the type of a function to assign to the parent object's property

Consider the following scenario: class SomeClass { public someProperty; public someMethodA(): void { this.someProperty = this.someMethodB() } public someMethodB() { ...some code... } } I need the type of somePropert ...

Using TypeScript with Node.js: the module is declaring a component locally, but it is not being exported

Within my nodeJS application, I have organized a models and seeders folder. One of the files within this structure is address.model.ts where I have defined the following schema: export {}; const mongoose = require('mongoose'); const addressS ...

What is the best way to specify the type of a property that has already been assigned

I am currently utilizing a third-party library that includes a type defined as the following: export interface ThirdPartyNodeType { id: string; name: string; data: any; } Upon further exploration, I have identified the content that I intend to include ...

The unit test is running successfully on the local environment, but it is failing on Jenkins with the error code TS2339, stating that the property 'toBeTruthy' is not recognized on the type 'Assertion'

I've been tackling a project in Angular and recently encountered an issue. Running 'npm run test' locally shows that my tests are passing without any problems. it('should create', () => { expect(component).toBeTruthy();}); How ...

After submitting a multi-image form from Angular, the "req" variable is not defined

I'm currently facing an issue with submitting a form from Angular 7 to a Node backend using Multer as middleware and Express.json() as bodyParser. While the text data is successfully transmitted to the backend, the image fields are appearing empty {}. ...

Issues arising from TypeScript error regarding the absence of a property on an object

Having a STEPS_CONFIG object that contains various steps with different properties, including defaultValues, I encountered an issue while trying to access the defaultValues property from the currentStep object in TypeScript. The error message indicated tha ...

Typescript error in RxJS: Incorrect argument type used

I came across this code snippet from an example in rxjs: Observable.fromEvent(this.getNativeElement(this.right), 'click') .map(event => 10) .startWith({x: 400, y: 400}) .scan((acc, curr) => Object.assign({}, acc, {x: acc ...

Having trouble retrieving information from combineLatest in Angular?

I'm having some trouble with fetching files to include in the post logs. It seems that the data is not being passed down the chain correctly when I attempt to use the pipe function after combining the latest data. This code snippet is part of a data r ...

Changing the appearance of a specific child component in React by referencing its id

There is an interface in my code. export interface DefaultFormList { defaultFormItems?: DefaultFormItems[]; } and export interface DefaultFormItems { id: string; name: string; formXml: string, isDefaultFormEnable: boolean; } I am looking ...

Sharing information between different components in React can be done using props, context, or a

When attempting to edit by clicking, the parent information is taken instead of creating a new VI. I was able to achieve this with angular dialog but I am unsure how to do it with components. This is done using dialog: <div class="dropdown-menu-item" ...