plugins/graphiql: add storage buckets to separate storage for different endpoints

This commit is contained in:
Patrik Oldsberg
2020-04-23 17:47:35 +02:00
parent dceff8dd2f
commit 9d080a4dce
4 changed files with 156 additions and 1 deletions
@@ -19,9 +19,11 @@ import { Tabs, Tab, makeStyles } from '@material-ui/core';
import { Page, pageTheme, Content, Header, HeaderLabel } from '@backstage/core';
import 'graphiql/graphiql.css';
import GraphiQL from 'graphiql';
import { StorageBucket } from 'lib/storage';
const tabs = [
{
id: 'gitlab',
title: 'GitLab',
fetcher: async (params: any) => {
const res = await fetch('https://gitlab.com/api/graphql', {
@@ -33,6 +35,7 @@ const tabs = [
},
},
{
id: 'countries',
title: 'Countries',
fetcher: async (params: any) => {
const res = await fetch('https://countries.trevorblades.com/', {
@@ -59,6 +62,9 @@ export const GraphiQLPage: FC<{}> = () => {
const classes = useStyles();
const [tabIndex, setTabIndex] = useState(0);
const { id, fetcher } = tabs[tabIndex];
const storage = StorageBucket.forLocalStorage(`plugin/graphiql/data/${id}`);
return (
<Page theme={pageTheme.tool}>
<Header title="GraphiQL">
@@ -71,7 +77,7 @@ export const GraphiQLPage: FC<{}> = () => {
<Tab key={index} label={title} value={index} />
))}
</Tabs>
<GraphiQL key={tabIndex} fetcher={tabs[tabIndex].fetcher} />
<GraphiQL key={tabIndex} fetcher={fetcher} storage={storage} />
</Content>
</Page>
);
@@ -0,0 +1,30 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { StorageBucket } from './StorageBucket';
describe('StorageBucket', () => {
it('should forbid access to unknown keys', () => {
const bucket = StorageBucket.forStorage(localStorage, 'hello');
expect(() => {
bucket['dunno-this-one'] = 'nope';
}).toThrow('Direct property access is not allowed for StorageBuckets');
expect(() => {
return bucket['dunno-this-one'];
}).toThrow('Direct property access is not allowed for StorageBuckets');
});
});
@@ -0,0 +1,102 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type BucketData = { [key in string]: string };
export class StorageBucket implements Storage {
private static noPropAccessProxyHandler: ProxyHandler<StorageBucket> = {
get(target, prop) {
if (prop in target) {
return target[prop as any];
}
throw new Error(
'Direct property access is not allowed for StorageBuckets',
);
},
set() {
throw new Error(
'Direct property access is not allowed for StorageBuckets',
);
},
};
static forStorage(storage: Storage, bucket: string) {
const storageBucket = new StorageBucket(storage, bucket);
return new Proxy(storageBucket, StorageBucket.noPropAccessProxyHandler);
}
static forLocalStorage(bucket: string): StorageBucket {
return StorageBucket.forStorage(localStorage, bucket);
}
private constructor(
private readonly storage: Storage,
private readonly bucket: string,
) {}
[name: string]: any;
get length(): number {
throw new Error('Method not implemented.');
}
clear(): void {
this.write({});
}
getItem(key: string): string | null {
return this.read()?.[key] ?? null;
}
key(): never {
throw new Error('Method not implemented.');
}
removeItem(key: string): void {
const data = this.read();
if (!data) {
return;
}
const deleted = delete data[key];
if (deleted) {
this.write(data);
}
}
setItem(key: string, value: string): void {
const data = this.read() ?? {};
data[key] = value;
this.write(data);
}
private read(): BucketData | undefined {
const bucketValue = this.storage.getItem(this.bucket);
if (!bucketValue) {
return undefined;
}
try {
return JSON.parse(bucketValue);
} catch {
return undefined;
}
}
private write(data: BucketData) {
this.storage.setItem(this.bucket, JSON.stringify(data));
}
}
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './StorageBucket';