Retrieve documents using shareId
This commit is contained in:
parent
22bc5a7373
commit
500d039856
|
@ -14,6 +14,12 @@ export const DEFAULT_PAGINATION_LIMIT = 25;
|
|||
|
||||
type Options = {
|
||||
ui: UiStore,
|
||||
errors: ErrorsStore,
|
||||
};
|
||||
|
||||
type FetchOptions = {
|
||||
prefetch?: boolean,
|
||||
shareId?: string,
|
||||
};
|
||||
|
||||
class DocumentsStore extends BaseStore {
|
||||
|
@ -166,15 +172,20 @@ class DocumentsStore extends BaseStore {
|
|||
|
||||
@action
|
||||
prefetchDocument = async (id: string) => {
|
||||
if (!this.getById(id)) this.fetch(id, true);
|
||||
if (!this.getById(id)) {
|
||||
this.fetch(id, { prefetch: true });
|
||||
}
|
||||
};
|
||||
|
||||
@action
|
||||
fetch = async (id: string, prefetch?: boolean): Promise<*> => {
|
||||
if (!prefetch) this.isFetching = true;
|
||||
fetch = async (id: string, options?: FetchOptions = {}): Promise<*> => {
|
||||
if (!options.prefetch) this.isFetching = true;
|
||||
|
||||
try {
|
||||
const res = await client.post('/documents.info', { id });
|
||||
const res = await client.post('/documents.info', {
|
||||
id,
|
||||
shareId: options.shareId,
|
||||
});
|
||||
invariant(res && res.data, 'Document not available');
|
||||
const { data } = res;
|
||||
const document = new Document(data);
|
||||
|
@ -186,7 +197,7 @@ class DocumentsStore extends BaseStore {
|
|||
|
||||
return document;
|
||||
} catch (e) {
|
||||
this.errors.add('Failed to load documents');
|
||||
this.errors.add('Failed to load document');
|
||||
} finally {
|
||||
this.isFetching = false;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import Sequelize from 'sequelize';
|
|||
import auth from './middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentDocument, presentRevision } from '../presenters';
|
||||
import { Document, Collection, Star, View, Revision } from '../models';
|
||||
import { Document, Collection, Share, Star, View, Revision } from '../models';
|
||||
import { InvalidRequestError } from '../errors';
|
||||
import events from '../events';
|
||||
import policy from '../policies';
|
||||
|
@ -157,12 +157,26 @@ router.post('documents.drafts', auth(), pagination(), async ctx => {
|
|||
};
|
||||
});
|
||||
|
||||
router.post('documents.info', auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
const document = await Document.findById(id);
|
||||
router.post('documents.info', auth({ required: false }), async ctx => {
|
||||
const { id, shareId } = ctx.body;
|
||||
ctx.assertPresent(id || shareId, 'id or shareId is required');
|
||||
|
||||
authorize(ctx.state.user, 'read', document);
|
||||
let document;
|
||||
if (shareId) {
|
||||
const share = await Share.findById(shareId, {
|
||||
include: [
|
||||
{
|
||||
model: Document,
|
||||
required: true,
|
||||
as: 'document',
|
||||
},
|
||||
],
|
||||
});
|
||||
document = share.document;
|
||||
} else {
|
||||
document = await Document.findById(id);
|
||||
authorize(ctx.state.user, 'read', document);
|
||||
}
|
||||
|
||||
ctx.body = {
|
||||
data: await presentDocument(ctx, document),
|
||||
|
|
|
@ -3,7 +3,7 @@ import TestServer from 'fetch-test-server';
|
|||
import app from '..';
|
||||
import { Document, View, Star, Revision } from '../models';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser } from '../test/factories';
|
||||
import { buildShare, buildUser } from '../test/factories';
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
|
@ -35,6 +35,22 @@ describe('#documents.info', async () => {
|
|||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
});
|
||||
|
||||
it('should return documents from shareId', async () => {
|
||||
const { user, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
teamId: document.teamId,
|
||||
});
|
||||
|
||||
const res = await server.post('/api/documents.info', {
|
||||
body: { token: user.getJwtToken(), shareId: share.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#documents.list', async () => {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { type Context } from 'koa';
|
|||
import { User, ApiKey } from '../../models';
|
||||
import { AuthenticationError, UserSuspendedError } from '../../errors';
|
||||
|
||||
export default function auth() {
|
||||
export default function auth(options?: { required?: boolean } = {}) {
|
||||
return async function authMiddleware(
|
||||
ctx: Context,
|
||||
next: () => Promise<void>
|
||||
|
@ -33,58 +33,61 @@ export default function auth() {
|
|||
token = ctx.request.query.token;
|
||||
}
|
||||
|
||||
if (!token) throw new AuthenticationError('Authentication required');
|
||||
if (!token && options.required !== false) {
|
||||
throw new AuthenticationError('Authentication required');
|
||||
}
|
||||
|
||||
let user;
|
||||
if (token) {
|
||||
if (String(token).match(/^[\w]{38}$/)) {
|
||||
// API key
|
||||
let apiKey;
|
||||
try {
|
||||
apiKey = await ApiKey.findOne({
|
||||
where: {
|
||||
secret: token,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
throw new AuthenticationError('Invalid API key');
|
||||
}
|
||||
|
||||
if (String(token).match(/^[\w]{38}$/)) {
|
||||
// API key
|
||||
let apiKey;
|
||||
try {
|
||||
apiKey = await ApiKey.findOne({
|
||||
where: {
|
||||
secret: token,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
throw new AuthenticationError('Invalid API key');
|
||||
if (!apiKey) throw new AuthenticationError('Invalid API key');
|
||||
|
||||
user = await User.findById(apiKey.userId);
|
||||
if (!user) throw new AuthenticationError('Invalid API key');
|
||||
} else {
|
||||
// JWT
|
||||
// Get user without verifying payload signature
|
||||
let payload;
|
||||
try {
|
||||
payload = JWT.decode(token);
|
||||
} catch (e) {
|
||||
throw new AuthenticationError('Unable to decode JWT token');
|
||||
}
|
||||
|
||||
if (!payload) throw new AuthenticationError('Invalid token');
|
||||
|
||||
user = await User.findById(payload.id);
|
||||
|
||||
try {
|
||||
JWT.verify(token, user.jwtSecret);
|
||||
} catch (e) {
|
||||
throw new AuthenticationError('Invalid token');
|
||||
}
|
||||
}
|
||||
|
||||
if (!apiKey) throw new AuthenticationError('Invalid API key');
|
||||
|
||||
user = await User.findById(apiKey.userId);
|
||||
if (!user) throw new AuthenticationError('Invalid API key');
|
||||
} else {
|
||||
// JWT
|
||||
// Get user without verifying payload signature
|
||||
let payload;
|
||||
try {
|
||||
payload = JWT.decode(token);
|
||||
} catch (e) {
|
||||
throw new AuthenticationError('Unable to decode JWT token');
|
||||
if (user.isSuspended) {
|
||||
const suspendingAdmin = await User.findById(user.suspendedById);
|
||||
throw new UserSuspendedError({ adminEmail: suspendingAdmin.email });
|
||||
}
|
||||
|
||||
if (!payload) throw new AuthenticationError('Invalid token');
|
||||
|
||||
user = await User.findById(payload.id);
|
||||
|
||||
try {
|
||||
JWT.verify(token, user.jwtSecret);
|
||||
} catch (e) {
|
||||
throw new AuthenticationError('Invalid token');
|
||||
}
|
||||
ctx.state.token = token;
|
||||
ctx.state.user = user;
|
||||
// $FlowFixMe
|
||||
ctx.cache[user.id] = user;
|
||||
}
|
||||
|
||||
if (user.isSuspended) {
|
||||
const suspendingAdmin = await User.findById(user.suspendedById);
|
||||
throw new UserSuspendedError({ adminEmail: suspendingAdmin.email });
|
||||
}
|
||||
|
||||
ctx.state.token = token;
|
||||
ctx.state.user = user;
|
||||
// $FlowFixMe
|
||||
ctx.cache[user.id] = user;
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
||||
|
|
Reference in New Issue