test: Use Components.js in SparqlStorage.

This commit is contained in:
Ruben Verborgh
2020-12-21 23:54:05 +01:00
parent 27910aaf73
commit 992267b4dc
5 changed files with 35 additions and 245 deletions

View File

@@ -1,66 +0,0 @@
import type {
DataAccessor,
HttpHandler,
ResourceStore,
} from '../../src/index';
import {
AllowEverythingAuthorizer,
AuthenticatedLdpHandler,
EmptyCredentialsExtractor,
MethodPermissionsExtractor,
QuadToRdfConverter,
RawBodyParser,
RdfToQuadConverter,
WaterfallHandler,
} from '../../src/index';
import type { ServerConfig } from './ServerConfig';
import {
getOperationHandler,
getConvertingStore,
getBasicRequestParser,
getDataAccessorStore,
getResponseWriter,
} from './Util';
/**
* DataAccessorBasedConfig works with
* - an AllowEverythingAuthorizer (no acl)
* - a DataAccessorBasedStore with a FileDataAccessor wrapped in a converting store (rdf to quad & quad to rdf)
* - GET, POST, PUT & DELETE operation handlers
*/
export class DataAccessorBasedConfig implements ServerConfig {
public store: ResourceStore;
public constructor(base: string, dataAccessor: DataAccessor, inType?: string) {
this.store = getConvertingStore(
getDataAccessorStore(base, dataAccessor),
[ new QuadToRdfConverter(), new RdfToQuadConverter() ],
inType,
);
}
public getHttpHandler(): HttpHandler {
// This is for the sake of test coverage, as it could also be just getBasicRequestParser()
const requestParser = getBasicRequestParser([ new RawBodyParser() ]);
const credentialsExtractor = new EmptyCredentialsExtractor();
const permissionsExtractor = new WaterfallHandler([
new MethodPermissionsExtractor(),
]);
const authorizer = new AllowEverythingAuthorizer();
const operationHandler = getOperationHandler(this.store);
const responseWriter = getResponseWriter();
const handler = new AuthenticatedLdpHandler({
requestParser,
credentialsExtractor,
permissionsExtractor,
authorizer,
operationHandler,
responseWriter,
});
return handler;
}
}

View File

@@ -1,7 +0,0 @@
import type { HttpHandler } from '../../src/server/HttpHandler';
import type { ResourceStore } from '../../src/storage/ResourceStore';
export interface ServerConfig {
store: ResourceStore;
getHttpHandler: () => HttpHandler;
}

View File

@@ -1,48 +1,6 @@
import { join } from 'path';
import * as Path from 'path';
import { Loader } from 'componentsjs';
import type {
BodyParser,
DataAccessor,
Operation,
RepresentationConverter,
ResourceStore,
ResponseDescription,
HttpResponse,
ResponseWriter,
OperationHandler,
} from '../../src/index';
import {
AcceptPreferenceParser,
BasicMetadataExtractor,
BasicRequestParser,
BasicResponseWriter,
BasicTargetExtractor,
ContentTypeParser,
DataAccessorBasedStore,
DeleteOperationHandler,
ErrorResponseWriter,
GetOperationHandler,
HeadOperationHandler,
LinkRelMetadataWriter,
LinkTypeParser,
MappedMetadataWriter,
PatchingStore,
PatchOperationHandler,
PostOperationHandler,
PutOperationHandler,
RawBodyParser,
RepresentationConvertingStore,
SequenceHandler,
SingleRootIdentifierStrategy,
SingleThreadedResourceLocker,
SlugParser,
SparqlUpdatePatchHandler,
UrlBasedAclManager,
WaterfallHandler,
WebAclAuthorizer,
} from '../../src/index';
import { CONTENT_TYPE, HTTP, RDF } from '../../src/util/UriConstants';
export const BASE = 'http://test.com';
@@ -52,122 +10,6 @@ export const BASE = 'http://test.com';
*/
export const getRootFilePath = (subfolder: string): string => join(__dirname, '../testData', subfolder);
/**
* Gives a data accessor store with the given data accessor.
* @param base - Base URL.
* @param dataAccessor - DataAccessor to use.
*
* @returns The data accessor based store.
*/
export const getDataAccessorStore = (base: string, dataAccessor: DataAccessor): DataAccessorBasedStore =>
new DataAccessorBasedStore(dataAccessor, new SingleRootIdentifierStrategy(base));
/**
* Gives a converting store given some converters.
* @param store - Initial store.
* @param converters - Converters to be used.
*
* @returns The converting store.
*/
export const getConvertingStore =
(store: ResourceStore, converters: RepresentationConverter[], inType?: string):
RepresentationConvertingStore =>
new RepresentationConvertingStore(store, {
inConverter: new WaterfallHandler(converters),
outConverter: new WaterfallHandler(converters),
inType,
});
/**
* Gives a patching store based on initial store.
* @param store - Initial resource store.
*
* @returns The patching store.
*/
export const getPatchingStore = (store: ResourceStore): PatchingStore => {
const locker = new SingleThreadedResourceLocker();
const patcher = new SparqlUpdatePatchHandler(store, locker);
return new PatchingStore(store, patcher);
};
/**
* Gives an operation handler given a store with all the common operation handlers.
* @param store - Initial resource store.
*
* @returns The operation handler.
*/
export const getOperationHandler = (store: ResourceStore): OperationHandler => {
const handlers = [
new GetOperationHandler(store),
new HeadOperationHandler(store),
new PostOperationHandler(store),
new PutOperationHandler(store),
new PatchOperationHandler(store),
new DeleteOperationHandler(store),
];
return new WaterfallHandler<Operation, ResponseDescription>(handlers);
};
export const getResponseWriter = (): ResponseWriter => {
const serializer = new SequenceHandler([
new MappedMetadataWriter({
[CONTENT_TYPE]: 'content-type',
[HTTP.location]: 'location',
}),
new LinkRelMetadataWriter({
[RDF.type]: 'type',
}),
]);
return new WaterfallHandler<{ response: HttpResponse; result: ResponseDescription | Error }, void>([
new ErrorResponseWriter(),
new BasicResponseWriter(serializer),
]);
};
/**
* Creates a BasicMetadataExtractor with parsers for content-type, slugs and link types.
*/
export const getBasicMetadataExtractor = (): BasicMetadataExtractor => new BasicMetadataExtractor([
new ContentTypeParser(),
new SlugParser(),
new LinkTypeParser(),
]);
/**
* Gives a basic request parser based on some body parses.
* @param bodyParsers - Optional list of body parsers, default is RawBodyParser.
*
* @returns The request parser.
*/
export const getBasicRequestParser = (bodyParsers: BodyParser[] = []): BasicRequestParser => {
let bodyParser: BodyParser;
if (bodyParsers.length === 1) {
bodyParser = bodyParsers[0];
} else if (bodyParsers.length === 0) {
// If no body parser is given (array is empty), default to RawBodyParser
bodyParser = new RawBodyParser();
} else {
bodyParser = new WaterfallHandler(bodyParsers);
}
return new BasicRequestParser({
targetExtractor: new BasicTargetExtractor(),
preferenceParser: new AcceptPreferenceParser(),
metadataExtractor: getBasicMetadataExtractor(),
bodyParser,
});
};
/**
* Gives a web acl authorizer based on a (default) runtimeConfig.
* @param store - Initial resource store.
* @param aclManager - Optional acl manager, default is UrlBasedAclManager.
*
* @returns The acl authorizer.
*/
export const getWebAclAuthorizer = (store: ResourceStore, aclManager = new UrlBasedAclManager()): WebAclAuthorizer =>
new WebAclAuthorizer(aclManager, store, new SingleRootIdentifierStrategy(BASE));
/**
* Returns a component instantiated from a Components.js configuration.
*/

View File

@@ -13,6 +13,7 @@
"files-scs:config/presets/representation-conversion.json",
"files-scs:config/presets/storage/backend/storage-memory.json",
"files-scs:config/presets/storage/backend/storage-filesystem.json",
"files-scs:config/presets/storage/backend/storage-sparql-endpoint.json",
"files-scs:config/presets/storage-wrapper.json",
"files-scs:config/presets/cli-params.json"
],
@@ -23,7 +24,13 @@
"RecordObject:_record": [
{
"RecordObject:_record_key": "initializer",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:AclInitializer" }
"RecordObject:_record_value": {
"@type": "SequenceHandler",
"SequenceHandler:_handlers": [
{ "@id": "urn:solid-server:default:RootContainerInitializer" },
{ "@id": "urn:solid-server:default:AclInitializer" }
]
}
},
{
"RecordObject:_record_key": "handler",

View File

@@ -1,23 +1,37 @@
import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
import { SparqlDataAccessor } from '../../src/storage/accessors/SparqlDataAccessor';
import { INTERNAL_QUADS } from '../../src/util/ContentTypes';
import { SingleRootIdentifierStrategy } from '../../src/util/identifiers/SingleRootIdentifierStrategy';
import { DataAccessorBasedConfig } from '../configs/DataAccessorBasedConfig';
import { BASE } from '../configs/Util';
import type { HttpHandler, Initializer, ResourceStore } from '../../src/';
import { BASE, instantiateFromConfig } from '../configs/Util';
import { describeIf, FileTestHelper } from '../util/TestHelpers';
describeIf('docker', 'a server with a SPARQL endpoint as storage', (): void => {
describe('without acl', (): void => {
const config = new DataAccessorBasedConfig(BASE,
new SparqlDataAccessor('http://localhost:4000/sparql', new SingleRootIdentifierStrategy(BASE)),
INTERNAL_QUADS);
const handler = config.getHttpHandler();
const fileHelper = new FileTestHelper(handler, new URL(BASE));
let handler: HttpHandler;
let fileHelper: FileTestHelper;
beforeAll(async(): Promise<void> => {
// Initialize store
const initializer = new RootContainerInitializer(BASE, config.store);
// Set up the internal store
const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': BASE,
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql',
};
const internalStore = await instantiateFromConfig(
'urn:solid-server:default:SparqlResourceStore',
'auth-ldp-handler.json',
variables,
) as ResourceStore;
variables['urn:solid-server:default:variable:store'] = internalStore;
// Create and initialize the HTTP handler and related components
let initializer: Initializer;
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
'auth-ldp-handler.json',
variables,
) as Record<string, any>;
({ handler, initializer } = instances);
await initializer.handleSafe();
// Create test helpers for manipulating the components
fileHelper = new FileTestHelper(handler, new URL(BASE));
});
it('can add a Turtle file to the store.', async():