fix: Support new ETag format in notification states

This commit is contained in:
Joachim Van Herwegen
2023-04-24 14:38:27 +02:00
parent 4ff6fe66ea
commit b250beaec9
5 changed files with 46 additions and 62 deletions

View File

@@ -1,6 +1,6 @@
import type { RepresentationMetadata } from '../http/representation/RepresentationMetadata';
import { DC } from '../util/Vocabularies';
import { getETag, isCurrentETag } from './Conditions';
import { getETag, sameResourceState } from './Conditions';
import type { Conditions } from './Conditions';
export interface BasicConditionsOptions {
@@ -39,19 +39,21 @@ export class BasicConditions implements Conditions {
return false;
}
// Helper function to see if an ETag matches the provided metadata
// eslint-disable-next-line func-style
let eTagMatches = (tag: string): boolean => isCurrentETag(tag, metadata);
if (strict) {
const eTag = getETag(metadata);
eTagMatches = (tag: string): boolean => tag === eTag;
}
const eTag = getETag(metadata);
if (eTag) {
// Helper function to see if an ETag matches the provided metadata
// eslint-disable-next-line func-style
let eTagMatches = (tag: string): boolean => sameResourceState(tag, eTag);
if (strict) {
eTagMatches = (tag: string): boolean => tag === eTag;
}
if (this.matchesETag && !this.matchesETag.includes('*') && !this.matchesETag.some(eTagMatches)) {
return false;
}
if (this.notMatchesETag?.some(eTagMatches)) {
return false;
if (this.matchesETag && !this.matchesETag.includes('*') && !this.matchesETag.some(eTagMatches)) {
return false;
}
if (this.notMatchesETag?.some(eTagMatches)) {
return false;
}
}
// In practice, this will only be undefined on a backend

View File

@@ -42,31 +42,21 @@ export interface Conditions {
export function getETag(metadata: RepresentationMetadata): string | undefined {
const modified = metadata.get(DC.terms.modified);
const { contentType } = metadata;
if (modified && contentType) {
if (modified) {
const date = new Date(modified.value);
return `"${date.getTime()}-${contentType}"`;
// It is possible for the content type to be undefined,
// such as when only the metadata returned by a `DataAccessor` is used.
return `"${date.getTime()}-${contentType ?? ''}"`;
}
}
/**
* Validates whether a given ETag corresponds to the current state of the resource,
* independent of the representation the ETag corresponds to.
* Validates whether 2 ETags correspond to the same state of a resource,
* independent of the representation the ETags correspond to.
* Assumes ETags are made with the {@link getETag} function.
* Since we base the ETag on the last modified date,
* we know the ETag still matches as long as that didn't change.
*
* @param eTag - ETag to validate.
* @param metadata - Metadata of the resource.
*
* @returns `true` if the ETag represents the current state of the resource.
*/
export function isCurrentETag(eTag: string, metadata: RepresentationMetadata): boolean {
const modified = metadata.get(DC.terms.modified);
if (!modified) {
return false;
}
const time = eTag.split('-', 1)[0];
const date = new Date(modified.value);
// `time` will still have the initial`"` of the ETag string
return time === `"${date.getTime()}`;
export function sameResourceState(eTag1: string, eTag2: string): boolean {
// Since we base the ETag on the last modified date,
// we know the ETags match as long as the date part is the same.
return eTag1.split('-')[0] === eTag2.split('-')[0];
}