Skip to content

Commit

Permalink
feat(protocol-http): add types for field (#4390)
Browse files Browse the repository at this point in the history
### Description
Adds `Field`, `FieldPosition`, and `Fields` types to `protocol-http`.

### Testing
`yarn test:all`
  • Loading branch information
milesziemer authored Feb 6, 2023
1 parent 6b1be0b commit 688b866
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 0 deletions.
80 changes: 80 additions & 0 deletions packages/protocol-http/src/Field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { FieldPosition } from "./FieldPosition";

export type FieldOptions = {
name: string;
kind?: FieldPosition
values?: string[];
};

/**
* A name-value pair representing a single field
* transmitted in an HTTP Request or Response.
*
* The kind will dictate metadata placement within
* an HTTP message.
*
* All field names are case insensitive and
* case-variance must be treated as equivalent.
* Names MAY be normalized but SHOULD be preserved
* for accuracy during transmission.
*/
export class Field {
public readonly name: string;
public readonly kind: FieldPosition;

public values: string[];

constructor({ name, kind = FieldPosition.HEADER, values = [] }: FieldOptions) {
this.name = name;
this.kind = kind;
this.values = values;
}

/**
* Appends a value to the field.
*
* @param value The value to append.
*/
public add(value: string): void {
this.values.push(value);
}

/**
* Overwrite existing field values.
*
* @param values The new field values.
*/
public set(values: string[]): void {
this.values = values;
}

/**
* Remove all matching entries from list.
*
* @param value Value to remove.
*/
public remove(value: string): void {
this.values = this.values.filter((v) => v !== value);
}

/**
* Get comma-delimited string.
*
* @returns String representation of {@link Field}.
*/
public toString(): string {
// Values with spaces or commas MUST be double-quoted
return this.values
.map((v) => (v.includes(",") || v.includes(" ") ? `"${v}"` : v))
.join(", ");
}

/**
* Get string values as a list
*
* @returns Values in {@link Field} as a list.
*/
public get(): string[] {
return this.values;
}
}
4 changes: 4 additions & 0 deletions packages/protocol-http/src/FieldPosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum FieldPosition {
HEADER,
TRAILER
}
59 changes: 59 additions & 0 deletions packages/protocol-http/src/Fields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Field } from "./Field";
import { FieldPosition } from "./FieldPosition";

export type FieldsOptions = { fields?: Field[]; encoding?: string; };

/**
* Collection of Field entries mapped by name.
*/
export class Fields {
private readonly entries: Record<string, Field> = {};
private readonly encoding: string;

constructor({ fields = [], encoding = "utf-8" }: FieldsOptions) {
fields.forEach(this.setField.bind(this));
this.encoding = encoding;
}

/**
* Set entry for a {@link Field} name. The `name`
* attribute will be used to key the collection.
*
* @param field The {@link Field} to set.
*/
public setField(field: Field): void {
this.entries[field.name] = field;
}

/**
* Retrieve {@link Field} entry by name.
*
* @param name The name of the {@link Field} entry
* to retrieve
* @returns The {@link Field} if it exists.
*/
public getField(name: string): Field | undefined {
return this.entries[name];
}

/**
* Delete entry from collection.
*
* @param name Name of the entry to delete.
*/
public removeField(name: string): void {
delete this.entries[name];
}

/**
* Helper function for retrieving specific types of fields.
* Used to grab all headers or all trailers.
*
* @param kind {@link FieldPosition} of entries to retrieve.
* @returns The {@link Field} entries with the specified
* {@link FieldPosition}.
*/
public getByType(kind: FieldPosition): Field[] {
return Object.values(this.entries).filter((field) => field.kind === kind);
}
}

0 comments on commit 688b866

Please sign in to comment.