mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
fix: Return 201 when creating new resources
This commit is contained in:
parent
5613ff9e71
commit
76c87bb56a
@ -2,6 +2,7 @@ import { getLoggerFor } from '../../logging/LogUtil';
|
|||||||
import type { ResourceStore } from '../../storage/ResourceStore';
|
import type { ResourceStore } from '../../storage/ResourceStore';
|
||||||
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
||||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||||
|
import { CreatedResponseDescription } from '../output/response/CreatedResponseDescription';
|
||||||
import { ResetResponseDescription } from '../output/response/ResetResponseDescription';
|
import { ResetResponseDescription } from '../output/response/ResetResponseDescription';
|
||||||
import type { ResponseDescription } from '../output/response/ResponseDescription';
|
import type { ResponseDescription } from '../output/response/ResponseDescription';
|
||||||
import type { Patch } from '../representation/Patch';
|
import type { Patch } from '../representation/Patch';
|
||||||
@ -36,7 +37,16 @@ export class PatchOperationHandler extends OperationHandler {
|
|||||||
this.logger.warn('PATCH requests require the Content-Type header to be set');
|
this.logger.warn('PATCH requests require the Content-Type header to be set');
|
||||||
throw new BadRequestHttpError('PATCH requests require the Content-Type header to be set');
|
throw new BadRequestHttpError('PATCH requests require the Content-Type header to be set');
|
||||||
}
|
}
|
||||||
|
// A more efficient approach would be to have the server return metadata indicating if a resource was new
|
||||||
|
// See https://github.com/solid/community-server/issues/632
|
||||||
|
// RFC7231, §4.3.4: If the target resource does not have a current representation and the
|
||||||
|
// PUT successfully creates one, then the origin server MUST inform the
|
||||||
|
// user agent by sending a 201 (Created) response.
|
||||||
|
const exists = await this.store.resourceExists(operation.target, operation.conditions);
|
||||||
await this.store.modifyResource(operation.target, operation.body as Patch, operation.conditions);
|
await this.store.modifyResource(operation.target, operation.body as Patch, operation.conditions);
|
||||||
return new ResetResponseDescription();
|
if (exists) {
|
||||||
|
return new ResetResponseDescription();
|
||||||
|
}
|
||||||
|
return new CreatedResponseDescription(operation.target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { getLoggerFor } from '../../logging/LogUtil';
|
|||||||
import type { ResourceStore } from '../../storage/ResourceStore';
|
import type { ResourceStore } from '../../storage/ResourceStore';
|
||||||
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
||||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||||
|
import { CreatedResponseDescription } from '../output/response/CreatedResponseDescription';
|
||||||
import { ResetResponseDescription } from '../output/response/ResetResponseDescription';
|
import { ResetResponseDescription } from '../output/response/ResetResponseDescription';
|
||||||
import type { ResponseDescription } from '../output/response/ResponseDescription';
|
import type { ResponseDescription } from '../output/response/ResponseDescription';
|
||||||
import type { OperationHandlerInput } from './OperationHandler';
|
import type { OperationHandlerInput } from './OperationHandler';
|
||||||
@ -35,7 +36,13 @@ export class PutOperationHandler extends OperationHandler {
|
|||||||
this.logger.warn('PUT requests require the Content-Type header to be set');
|
this.logger.warn('PUT requests require the Content-Type header to be set');
|
||||||
throw new BadRequestHttpError('PUT requests require the Content-Type header to be set');
|
throw new BadRequestHttpError('PUT requests require the Content-Type header to be set');
|
||||||
}
|
}
|
||||||
|
// A more efficient approach would be to have the server return metadata indicating if a resource was new
|
||||||
|
// See https://github.com/solid/community-server/issues/632
|
||||||
|
const exists = await this.store.resourceExists(operation.target, operation.conditions);
|
||||||
await this.store.setRepresentation(operation.target, operation.body, operation.conditions);
|
await this.store.setRepresentation(operation.target, operation.body, operation.conditions);
|
||||||
return new ResetResponseDescription();
|
if (exists) {
|
||||||
|
return new ResetResponseDescription();
|
||||||
|
}
|
||||||
|
return new CreatedResponseDescription(operation.target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,8 @@ describe.each(configs)('A dynamic pod server with template config %s', (template
|
|||||||
},
|
},
|
||||||
body: 'this is new data!',
|
body: 'this is new data!',
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(205);
|
expect(res.status).toBe(201);
|
||||||
|
expect(res.headers.get('location')).toBe(`${podUrl}test`);
|
||||||
|
|
||||||
res = await fetch(`${podUrl}test`, {
|
res = await fetch(`${podUrl}test`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -84,7 +84,7 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig,
|
|||||||
|
|
||||||
// PUT
|
// PUT
|
||||||
const document = `${baseUrl}test.txt`;
|
const document = `${baseUrl}test.txt`;
|
||||||
await putResource(document, { contentType: 'text/plain', body: 'TESTDATA' });
|
await putResource(document, { contentType: 'text/plain', body: 'TESTDATA', exists: false });
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
const response = await getResource(document);
|
const response = await getResource(document);
|
||||||
|
@ -110,7 +110,7 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
|
|||||||
await expect(response.text()).resolves.toBe('TESTFILE0');
|
await expect(response.text()).resolves.toBe('TESTFILE0');
|
||||||
|
|
||||||
// PUT
|
// PUT
|
||||||
await putResource(documentUrl, { contentType: 'text/plain', body: 'TESTFILE1' });
|
await putResource(documentUrl, { contentType: 'text/plain', body: 'TESTFILE1', exists: true });
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
response = await getResource(documentUrl, {}, { contentType: 'text/plain' });
|
response = await getResource(documentUrl, {}, { contentType: 'text/plain' });
|
||||||
@ -253,7 +253,8 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
|
|||||||
},
|
},
|
||||||
body: createReadStream(joinFilePath(__dirname, '../assets/testimage.png')) as any,
|
body: createReadStream(joinFilePath(__dirname, '../assets/testimage.png')) as any,
|
||||||
});
|
});
|
||||||
expect(response.status).toBe(205);
|
expect(response.status).toBe(201);
|
||||||
|
expect(response.headers.get('location')).toBe(documentUrl);
|
||||||
await expect(response.text()).resolves.toHaveLength(0);
|
await expect(response.text()).resolves.toHaveLength(0);
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
@ -291,7 +292,8 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
|
|||||||
headers: { 'content-length': '0', 'content-type': 'text/turtle' },
|
headers: { 'content-length': '0', 'content-type': 'text/turtle' },
|
||||||
body: '',
|
body: '',
|
||||||
});
|
});
|
||||||
expect(response.status).toBe(205);
|
expect(response.status).toBe(201);
|
||||||
|
expect(response.headers.get('location')).toBe(documentUrl);
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
await getResource(documentUrl);
|
await getResource(documentUrl);
|
||||||
@ -312,7 +314,7 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
|
|||||||
'INSERT {<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>}',
|
'INSERT {<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>}',
|
||||||
'WHERE {}',
|
'WHERE {}',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
await patchResource(documentUrl, query);
|
await patchResource(documentUrl, query, true);
|
||||||
|
|
||||||
// PATCH using a content-type header with charset
|
// PATCH using a content-type header with charset
|
||||||
const query2 = [ 'DELETE { <http://test.com/s2> <http://test.com/p2> <http://test.com/o2> }',
|
const query2 = [ 'DELETE { <http://test.com/s2> <http://test.com/p2> <http://test.com/o2> }',
|
||||||
@ -361,7 +363,7 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
|
|||||||
'INSERT {<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>}',
|
'INSERT {<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>}',
|
||||||
'WHERE {}',
|
'WHERE {}',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
await patchResource(documentUrl, query);
|
await patchResource(documentUrl, query, true);
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
response = await getResource(documentUrl);
|
response = await getResource(documentUrl);
|
||||||
|
@ -39,7 +39,7 @@ describeIf('docker', 'A server with a RedisResourceLocker as ResourceLocker', ()
|
|||||||
},
|
},
|
||||||
body: fileData,
|
body: fileData,
|
||||||
});
|
});
|
||||||
expect(response.status).toBe(205);
|
expect(response.status).toBe(201);
|
||||||
|
|
||||||
// Get file
|
// Get file
|
||||||
response = await fetch(fileUrl);
|
response = await fetch(fileUrl);
|
||||||
@ -64,7 +64,7 @@ describeIf('docker', 'A server with a RedisResourceLocker as ResourceLocker', ()
|
|||||||
'content-type': 'text/plain',
|
'content-type': 'text/plain',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(response.status).toBe(205);
|
expect(response.status).toBe(201);
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
response = await fetch(containerUrl);
|
response = await fetch(containerUrl);
|
||||||
@ -88,7 +88,7 @@ describeIf('docker', 'A server with a RedisResourceLocker as ResourceLocker', ()
|
|||||||
},
|
},
|
||||||
body: fileData,
|
body: fileData,
|
||||||
});
|
});
|
||||||
expect(response.status).toBe(205);
|
expect(response.status).toBe(201);
|
||||||
|
|
||||||
// GET 4 times
|
// GET 4 times
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
|
@ -76,7 +76,8 @@ describe('A server with restricted IDP access', (): void => {
|
|||||||
headers: { 'content-type': 'text/turtle' },
|
headers: { 'content-type': 'text/turtle' },
|
||||||
body: restrictedAcl,
|
body: restrictedAcl,
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(205);
|
expect(res.status).toBe(201);
|
||||||
|
expect(res.headers.get('location')).toBe(`${baseUrl}idp/register/.acl`);
|
||||||
|
|
||||||
// Registration is now disabled
|
// Registration is now disabled
|
||||||
res = await fetch(`${baseUrl}idp/register/`);
|
res = await fetch(`${baseUrl}idp/register/`);
|
||||||
|
@ -77,7 +77,8 @@ describe('A Solid server', (): void => {
|
|||||||
},
|
},
|
||||||
body: '<a:b> <a:b> <a:b>.',
|
body: '<a:b> <a:b> <a:b>.',
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(205);
|
expect(res.status).toBe(201);
|
||||||
|
expect(res.headers.get('location')).toBe(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can PUT to resources.', async(): Promise<void> => {
|
it('can PUT to resources.', async(): Promise<void> => {
|
||||||
@ -89,7 +90,8 @@ describe('A Solid server', (): void => {
|
|||||||
},
|
},
|
||||||
body: '<a:b> <a:b> <a:b>.',
|
body: '<a:b> <a:b> <a:b>.',
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(205);
|
expect(res.status).toBe(201);
|
||||||
|
expect(res.headers.get('location')).toBe(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can handle PUT errors.', async(): Promise<void> => {
|
it('can handle PUT errors.', async(): Promise<void> => {
|
||||||
|
@ -79,7 +79,8 @@ describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardo
|
|||||||
},
|
},
|
||||||
body: 'this is new data!',
|
body: 'this is new data!',
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(205);
|
expect(res.status).toBe(201);
|
||||||
|
expect(res.headers.get('location')).toBe(`${baseUrl}alice`);
|
||||||
|
|
||||||
res = await fetch(`${baseUrl}alice`);
|
res = await fetch(`${baseUrl}alice`);
|
||||||
expect(res.status).toBe(200);
|
expect(res.status).toBe(200);
|
||||||
@ -136,7 +137,8 @@ describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardo
|
|||||||
},
|
},
|
||||||
body: 'this is new data!',
|
body: 'this is new data!',
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(205);
|
expect(res.status).toBe(201);
|
||||||
|
expect(res.headers.get('location')).toBe(`${podUrl}alice`);
|
||||||
|
|
||||||
res = await fetch(`${baseUrl}alice`, {
|
res = await fetch(`${baseUrl}alice`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -6,17 +6,24 @@ import { BasicConditions } from '../../../../src/storage/BasicConditions';
|
|||||||
import type { ResourceStore } from '../../../../src/storage/ResourceStore';
|
import type { ResourceStore } from '../../../../src/storage/ResourceStore';
|
||||||
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
|
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
|
||||||
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
||||||
|
import { SOLID_HTTP } from '../../../../src/util/Vocabularies';
|
||||||
|
|
||||||
describe('A PatchOperationHandler', (): void => {
|
describe('A PatchOperationHandler', (): void => {
|
||||||
let operation: Operation;
|
let operation: Operation;
|
||||||
let body: Representation;
|
let body: Representation;
|
||||||
const conditions = new BasicConditions({});
|
const conditions = new BasicConditions({});
|
||||||
const store = {} as unknown as ResourceStore;
|
let store: jest.Mocked<ResourceStore>;
|
||||||
const handler = new PatchOperationHandler(store);
|
let handler: PatchOperationHandler;
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
body = new BasicRepresentation('', 'text/turtle');
|
body = new BasicRepresentation('', 'text/turtle');
|
||||||
operation = { method: 'PATCH', target: { path: 'http://test.com/foo' }, body, conditions, preferences: {}};
|
operation = { method: 'PATCH', target: { path: 'http://test.com/foo' }, body, conditions, preferences: {}};
|
||||||
store.modifyResource = jest.fn(async(): Promise<any> => undefined);
|
|
||||||
|
store = {
|
||||||
|
resourceExists: jest.fn(),
|
||||||
|
modifyResource: jest.fn(),
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
handler = new PatchOperationHandler(store);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('only supports PATCH operations.', async(): Promise<void> => {
|
it('only supports PATCH operations.', async(): Promise<void> => {
|
||||||
@ -30,7 +37,17 @@ describe('A PatchOperationHandler', (): void => {
|
|||||||
await expect(handler.handle({ operation })).rejects.toThrow(BadRequestHttpError);
|
await expect(handler.handle({ operation })).rejects.toThrow(BadRequestHttpError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deletes the resource from the store and returns the correct response.', async(): Promise<void> => {
|
it('creates the representation in the store and returns the correct response.', async(): Promise<void> => {
|
||||||
|
const result = await handler.handle({ operation });
|
||||||
|
expect(store.modifyResource).toHaveBeenCalledTimes(1);
|
||||||
|
expect(store.modifyResource).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
||||||
|
expect(result.statusCode).toBe(201);
|
||||||
|
expect(result.metadata?.get(SOLID_HTTP.location)?.value).toBe(operation.target.path);
|
||||||
|
expect(result.data).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct response if the resource already exists.', async(): Promise<void> => {
|
||||||
|
store.resourceExists.mockResolvedValueOnce(true);
|
||||||
const result = await handler.handle({ operation });
|
const result = await handler.handle({ operation });
|
||||||
expect(store.modifyResource).toHaveBeenCalledTimes(1);
|
expect(store.modifyResource).toHaveBeenCalledTimes(1);
|
||||||
expect(store.modifyResource).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
expect(store.modifyResource).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
||||||
|
@ -6,18 +6,23 @@ import { BasicConditions } from '../../../../src/storage/BasicConditions';
|
|||||||
import type { ResourceStore } from '../../../../src/storage/ResourceStore';
|
import type { ResourceStore } from '../../../../src/storage/ResourceStore';
|
||||||
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
|
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
|
||||||
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
||||||
|
import { SOLID_HTTP } from '../../../../src/util/Vocabularies';
|
||||||
|
|
||||||
describe('A PutOperationHandler', (): void => {
|
describe('A PutOperationHandler', (): void => {
|
||||||
let operation: Operation;
|
let operation: Operation;
|
||||||
let body: Representation;
|
let body: Representation;
|
||||||
const conditions = new BasicConditions({});
|
const conditions = new BasicConditions({});
|
||||||
const store = {} as unknown as ResourceStore;
|
let store: jest.Mocked<ResourceStore>;
|
||||||
const handler = new PutOperationHandler(store);
|
let handler: PutOperationHandler;
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
body = new BasicRepresentation('', 'text/turtle');
|
body = new BasicRepresentation('', 'text/turtle');
|
||||||
operation = { method: 'PUT', target: { path: 'http://test.com/foo' }, body, conditions, preferences: {}};
|
operation = { method: 'PUT', target: { path: 'http://test.com/foo' }, body, conditions, preferences: {}};
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
store = {
|
||||||
store.setRepresentation = jest.fn(async(): Promise<any> => {});
|
resourceExists: jest.fn(),
|
||||||
|
setRepresentation: jest.fn(),
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
handler = new PutOperationHandler(store);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('only supports PUT operations.', async(): Promise<void> => {
|
it('only supports PUT operations.', async(): Promise<void> => {
|
||||||
@ -31,7 +36,17 @@ describe('A PutOperationHandler', (): void => {
|
|||||||
await expect(handler.handle({ operation })).rejects.toThrow(BadRequestHttpError);
|
await expect(handler.handle({ operation })).rejects.toThrow(BadRequestHttpError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the representation in the store and returns the correct response.', async(): Promise<void> => {
|
it('creates the representation in the store and returns the correct response.', async(): Promise<void> => {
|
||||||
|
const result = await handler.handle({ operation });
|
||||||
|
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
||||||
|
expect(store.setRepresentation).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
||||||
|
expect(result.statusCode).toBe(201);
|
||||||
|
expect(result.metadata?.get(SOLID_HTTP.location)?.value).toBe(operation.target.path);
|
||||||
|
expect(result.data).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct response if the resource already exists.', async(): Promise<void> => {
|
||||||
|
store.resourceExists.mockResolvedValueOnce(true);
|
||||||
const result = await handler.handle({ operation });
|
const result = await handler.handle({ operation });
|
||||||
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
||||||
expect(store.setRepresentation).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
expect(store.setRepresentation).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
||||||
|
@ -36,7 +36,7 @@ export async function getResource(url: string,
|
|||||||
/**
|
/**
|
||||||
* This is specifically for PUT requests which are expected to succeed.
|
* This is specifically for PUT requests which are expected to succeed.
|
||||||
*/
|
*/
|
||||||
export async function putResource(url: string, options: { contentType: string; body?: string }):
|
export async function putResource(url: string, options: { contentType: string; body?: string; exists?: boolean }):
|
||||||
Promise<Response> {
|
Promise<Response> {
|
||||||
const init: RequestInit = {
|
const init: RequestInit = {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@ -47,7 +47,10 @@ Promise<Response> {
|
|||||||
(init.headers as Record<string, string>).link = '<http://www.w3.org/ns/ldp#Container>; rel="type"';
|
(init.headers as Record<string, string>).link = '<http://www.w3.org/ns/ldp#Container>; rel="type"';
|
||||||
}
|
}
|
||||||
const response = await fetch(url, init);
|
const response = await fetch(url, init);
|
||||||
expect(response.status).toBe(205);
|
expect(response.status).toBe(options.exists ? 205 : 201);
|
||||||
|
if (!options.exists) {
|
||||||
|
expect(response.headers.get('location')).toBe(url);
|
||||||
|
}
|
||||||
await expect(response.text()).resolves.toHaveLength(0);
|
await expect(response.text()).resolves.toHaveLength(0);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -84,7 +87,7 @@ export async function postResource(container: string, options: CreateOptions): P
|
|||||||
/**
|
/**
|
||||||
* This is specifically for PATCH requests which are expected to succeed.
|
* This is specifically for PATCH requests which are expected to succeed.
|
||||||
*/
|
*/
|
||||||
export async function patchResource(url: string, query: string): Promise<Response> {
|
export async function patchResource(url: string, query: string, exists?: boolean): Promise<Response> {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {
|
headers: {
|
||||||
@ -93,7 +96,10 @@ export async function patchResource(url: string, query: string): Promise<Respons
|
|||||||
body: query,
|
body: query,
|
||||||
});
|
});
|
||||||
await expect(response.text()).resolves.toHaveLength(0);
|
await expect(response.text()).resolves.toHaveLength(0);
|
||||||
expect(response.status).toBe(205);
|
expect(response.status).toBe(exists ? 205 : 201);
|
||||||
|
if (!exists) {
|
||||||
|
expect(response.headers.get('location')).toBe(url);
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user