* Generic call function mutation * Change function return type to any * First passing test * Testing errors * Testing different data types
5247 lines
171 KiB
JavaScript
5247 lines
171 KiB
JavaScript
const http = require('http');
|
|
const express = require('express');
|
|
const req = require('../lib/request');
|
|
const fetch = require('node-fetch');
|
|
const FormData = require('form-data');
|
|
const ws = require('ws');
|
|
const { getMainDefinition } = require('apollo-utilities');
|
|
const { ApolloLink, split } = require('apollo-link');
|
|
const { createHttpLink } = require('apollo-link-http');
|
|
const { InMemoryCache } = require('apollo-cache-inmemory');
|
|
const { createUploadLink } = require('apollo-upload-client');
|
|
const { SubscriptionClient } = require('subscriptions-transport-ws');
|
|
const { WebSocketLink } = require('apollo-link-ws');
|
|
const ApolloClient = require('apollo-client').default;
|
|
const gql = require('graphql-tag');
|
|
const { ParseServer } = require('../');
|
|
const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer');
|
|
const ReadPreference = require('mongodb').ReadPreference;
|
|
|
|
describe('ParseGraphQLServer', () => {
|
|
let parseServer;
|
|
let parseGraphQLServer;
|
|
|
|
beforeAll(async () => {
|
|
parseServer = await global.reconfigureServer({});
|
|
parseGraphQLServer = new ParseGraphQLServer(parseServer, {
|
|
graphQLPath: '/graphql',
|
|
playgroundPath: '/playground',
|
|
subscriptionsPath: '/subscriptions',
|
|
});
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('should require a parseServer instance', () => {
|
|
expect(() => new ParseGraphQLServer()).toThrow(
|
|
'You must provide a parseServer instance!'
|
|
);
|
|
});
|
|
|
|
it('should require config.graphQLPath', () => {
|
|
expect(() => new ParseGraphQLServer(parseServer)).toThrow(
|
|
'You must provide a config.graphQLPath!'
|
|
);
|
|
expect(() => new ParseGraphQLServer(parseServer, {})).toThrow(
|
|
'You must provide a config.graphQLPath!'
|
|
);
|
|
});
|
|
|
|
it('should only require parseServer and config.graphQLPath args', () => {
|
|
let parseGraphQLServer;
|
|
expect(() => {
|
|
parseGraphQLServer = new ParseGraphQLServer(parseServer, {
|
|
graphQLPath: 'graphql',
|
|
});
|
|
}).not.toThrow();
|
|
expect(parseGraphQLServer.parseGraphQLSchema).toBeDefined();
|
|
expect(parseGraphQLServer.parseGraphQLSchema.databaseController).toEqual(
|
|
parseServer.config.databaseController
|
|
);
|
|
});
|
|
|
|
it('should initialize parseGraphQLSchema with a log controller', async () => {
|
|
const loggerAdapter = {
|
|
log: () => {},
|
|
error: () => {},
|
|
};
|
|
const parseServer = await global.reconfigureServer({
|
|
loggerAdapter,
|
|
});
|
|
const parseGraphQLServer = new ParseGraphQLServer(parseServer, {
|
|
graphQLPath: 'graphql',
|
|
});
|
|
expect(parseGraphQLServer.parseGraphQLSchema.log.adapter).toBe(
|
|
loggerAdapter
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('_getGraphQLOptions', () => {
|
|
const req = {
|
|
info: new Object(),
|
|
config: new Object(),
|
|
auth: new Object(),
|
|
};
|
|
|
|
it("should return schema and context with req's info, config and auth", async () => {
|
|
const options = await parseGraphQLServer._getGraphQLOptions(req);
|
|
expect(options.schema).toEqual(
|
|
parseGraphQLServer.parseGraphQLSchema.graphQLSchema
|
|
);
|
|
expect(options.context.info).toEqual(req.info);
|
|
expect(options.context.config).toEqual(req.config);
|
|
expect(options.context.auth).toEqual(req.auth);
|
|
});
|
|
|
|
it('should load GraphQL schema in every call', async () => {
|
|
const originalLoad = parseGraphQLServer.parseGraphQLSchema.load;
|
|
let counter = 0;
|
|
parseGraphQLServer.parseGraphQLSchema.load = () => ++counter;
|
|
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(
|
|
1
|
|
);
|
|
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(
|
|
2
|
|
);
|
|
expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(
|
|
3
|
|
);
|
|
parseGraphQLServer.parseGraphQLSchema.load = originalLoad;
|
|
});
|
|
});
|
|
|
|
describe('applyGraphQL', () => {
|
|
it('should require an Express.js app instance', () => {
|
|
expect(() => parseGraphQLServer.applyGraphQL()).toThrow(
|
|
'You must provide an Express.js app instance!'
|
|
);
|
|
expect(() => parseGraphQLServer.applyGraphQL({})).toThrow(
|
|
'You must provide an Express.js app instance!'
|
|
);
|
|
expect(() =>
|
|
parseGraphQLServer.applyGraphQL(new express())
|
|
).not.toThrow();
|
|
});
|
|
|
|
it('should apply middlewares at config.graphQLPath', () => {
|
|
let useCount = 0;
|
|
expect(() =>
|
|
new ParseGraphQLServer(parseServer, {
|
|
graphQLPath: 'somepath',
|
|
}).applyGraphQL({
|
|
use: path => {
|
|
useCount++;
|
|
expect(path).toEqual('somepath');
|
|
},
|
|
})
|
|
).not.toThrow();
|
|
expect(useCount).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('applyPlayground', () => {
|
|
it('should require an Express.js app instance', () => {
|
|
expect(() => parseGraphQLServer.applyPlayground()).toThrow(
|
|
'You must provide an Express.js app instance!'
|
|
);
|
|
expect(() => parseGraphQLServer.applyPlayground({})).toThrow(
|
|
'You must provide an Express.js app instance!'
|
|
);
|
|
expect(() =>
|
|
parseGraphQLServer.applyPlayground(new express())
|
|
).not.toThrow();
|
|
});
|
|
|
|
it('should require initialization with config.playgroundPath', () => {
|
|
expect(() =>
|
|
new ParseGraphQLServer(parseServer, {
|
|
graphQLPath: 'graphql',
|
|
}).applyPlayground(new express())
|
|
).toThrow('You must provide a config.playgroundPath to applyPlayground!');
|
|
});
|
|
|
|
it('should apply middlewares at config.playgroundPath', () => {
|
|
let useCount = 0;
|
|
expect(() =>
|
|
new ParseGraphQLServer(parseServer, {
|
|
graphQLPath: 'graphQL',
|
|
playgroundPath: 'somepath',
|
|
}).applyPlayground({
|
|
get: path => {
|
|
useCount++;
|
|
expect(path).toEqual('somepath');
|
|
},
|
|
})
|
|
).not.toThrow();
|
|
expect(useCount).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('createSubscriptions', () => {
|
|
it('should require initialization with config.subscriptionsPath', () => {
|
|
expect(() =>
|
|
new ParseGraphQLServer(parseServer, {
|
|
graphQLPath: 'graphql',
|
|
}).createSubscriptions({})
|
|
).toThrow(
|
|
'You must provide a config.subscriptionsPath to createSubscriptions!'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('API', () => {
|
|
const headers = {
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Javascript-Key': 'test',
|
|
};
|
|
|
|
let apolloClient;
|
|
|
|
let user1;
|
|
let user2;
|
|
let user3;
|
|
let user4;
|
|
let user5;
|
|
let role;
|
|
let object1;
|
|
let object2;
|
|
let object3;
|
|
let object4;
|
|
let objects = [];
|
|
|
|
async function prepareData() {
|
|
user1 = new Parse.User();
|
|
user1.setUsername('user1');
|
|
user1.setPassword('user1');
|
|
await user1.signUp();
|
|
|
|
user2 = new Parse.User();
|
|
user2.setUsername('user2');
|
|
user2.setPassword('user2');
|
|
await user2.signUp();
|
|
|
|
user3 = new Parse.User();
|
|
user3.setUsername('user3');
|
|
user3.setPassword('user3');
|
|
await user3.signUp();
|
|
|
|
user4 = new Parse.User();
|
|
user4.setUsername('user4');
|
|
user4.setPassword('user4');
|
|
await user4.signUp();
|
|
|
|
user5 = new Parse.User();
|
|
user5.setUsername('user5');
|
|
user5.setPassword('user5');
|
|
await user5.signUp();
|
|
|
|
const roleACL = new Parse.ACL();
|
|
roleACL.setPublicReadAccess(true);
|
|
role = new Parse.Role();
|
|
role.setName('role');
|
|
role.setACL(roleACL);
|
|
role.getUsers().add(user1);
|
|
role.getUsers().add(user3);
|
|
role = await role.save();
|
|
|
|
const schemaController = await parseServer.config.databaseController.loadSchema();
|
|
try {
|
|
await schemaController.addClassIfNotExists(
|
|
'GraphQLClass',
|
|
{
|
|
someField: { type: 'String' },
|
|
pointerToUser: { type: 'Pointer', targetClass: '_User' },
|
|
},
|
|
{
|
|
find: {
|
|
'role:role': true,
|
|
[user1.id]: true,
|
|
[user2.id]: true,
|
|
},
|
|
create: {
|
|
'role:role': true,
|
|
[user1.id]: true,
|
|
[user2.id]: true,
|
|
},
|
|
get: {
|
|
'role:role': true,
|
|
[user1.id]: true,
|
|
[user2.id]: true,
|
|
},
|
|
update: {
|
|
'role:role': true,
|
|
[user1.id]: true,
|
|
[user2.id]: true,
|
|
},
|
|
addField: {
|
|
'role:role': true,
|
|
[user1.id]: true,
|
|
[user2.id]: true,
|
|
},
|
|
delete: {
|
|
'role:role': true,
|
|
[user1.id]: true,
|
|
[user2.id]: true,
|
|
},
|
|
readUserFields: ['pointerToUser'],
|
|
writeUserFields: ['pointerToUser'],
|
|
},
|
|
{}
|
|
);
|
|
} catch (err) {
|
|
if (
|
|
!(err instanceof Parse.Error) ||
|
|
err.message !== 'Class GraphQLClass already exists.'
|
|
) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
object1 = new Parse.Object('GraphQLClass');
|
|
object1.set('someField', 'someValue1');
|
|
const object1ACL = new Parse.ACL();
|
|
object1ACL.setPublicReadAccess(false);
|
|
object1ACL.setPublicWriteAccess(false);
|
|
object1ACL.setRoleReadAccess(role, true);
|
|
object1ACL.setRoleWriteAccess(role, true);
|
|
object1ACL.setReadAccess(user1.id, true);
|
|
object1ACL.setWriteAccess(user1.id, true);
|
|
object1ACL.setReadAccess(user2.id, true);
|
|
object1ACL.setWriteAccess(user2.id, true);
|
|
object1.setACL(object1ACL);
|
|
await object1.save(undefined, { useMasterKey: true });
|
|
|
|
object2 = new Parse.Object('GraphQLClass');
|
|
object2.set('someField', 'someValue2');
|
|
const object2ACL = new Parse.ACL();
|
|
object2ACL.setPublicReadAccess(false);
|
|
object2ACL.setPublicWriteAccess(false);
|
|
object2ACL.setReadAccess(user1.id, true);
|
|
object2ACL.setWriteAccess(user1.id, true);
|
|
object2ACL.setReadAccess(user2.id, true);
|
|
object2ACL.setWriteAccess(user2.id, true);
|
|
object2ACL.setReadAccess(user5.id, true);
|
|
object2ACL.setWriteAccess(user5.id, true);
|
|
object2.setACL(object2ACL);
|
|
await object2.save(undefined, { useMasterKey: true });
|
|
|
|
object3 = new Parse.Object('GraphQLClass');
|
|
object3.set('someField', 'someValue3');
|
|
object3.set('pointerToUser', user5);
|
|
await object3.save(undefined, { useMasterKey: true });
|
|
|
|
object4 = new Parse.Object('PublicClass');
|
|
object4.set('someField', 'someValue4');
|
|
await object4.save();
|
|
|
|
objects = [];
|
|
objects.push(object1, object2, object3, object4);
|
|
}
|
|
|
|
beforeAll(async () => {
|
|
const expressApp = express();
|
|
const httpServer = http.createServer(expressApp);
|
|
expressApp.use('/parse', parseServer.app);
|
|
ParseServer.createLiveQueryServer(httpServer, {
|
|
port: 1338,
|
|
});
|
|
parseGraphQLServer.applyGraphQL(expressApp);
|
|
parseGraphQLServer.applyPlayground(expressApp);
|
|
parseGraphQLServer.createSubscriptions(httpServer);
|
|
await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
|
|
|
|
const subscriptionClient = new SubscriptionClient(
|
|
'ws://localhost:13377/subscriptions',
|
|
{
|
|
reconnect: true,
|
|
connectionParams: headers,
|
|
},
|
|
ws
|
|
);
|
|
const wsLink = new WebSocketLink(subscriptionClient);
|
|
const httpLink = createUploadLink({
|
|
uri: 'http://localhost:13377/graphql',
|
|
fetch,
|
|
headers,
|
|
});
|
|
apolloClient = new ApolloClient({
|
|
link: split(
|
|
({ query }) => {
|
|
const { kind, operation } = getMainDefinition(query);
|
|
return (
|
|
kind === 'OperationDefinition' && operation === 'subscription'
|
|
);
|
|
},
|
|
wsLink,
|
|
httpLink
|
|
),
|
|
cache: new InMemoryCache(),
|
|
defaultOptions: {
|
|
query: {
|
|
fetchPolicy: 'no-cache',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
describe('GraphQL', () => {
|
|
it('should be healthy', async () => {
|
|
const health = (await apolloClient.query({
|
|
query: gql`
|
|
query Health {
|
|
health
|
|
}
|
|
`,
|
|
})).data.health;
|
|
expect(health).toBeTruthy();
|
|
});
|
|
|
|
it('should be cors enabled', async () => {
|
|
let checked = false;
|
|
const apolloClient = new ApolloClient({
|
|
link: new ApolloLink((operation, forward) => {
|
|
return forward(operation).map(response => {
|
|
const context = operation.getContext();
|
|
const {
|
|
response: { headers },
|
|
} = context;
|
|
expect(headers.get('access-control-allow-origin')).toEqual('*');
|
|
checked = true;
|
|
return response;
|
|
});
|
|
}).concat(
|
|
createHttpLink({
|
|
uri: 'http://localhost:13377/graphql',
|
|
fetch,
|
|
headers: {
|
|
...headers,
|
|
Origin: 'http://someorigin.com',
|
|
},
|
|
})
|
|
),
|
|
cache: new InMemoryCache(),
|
|
});
|
|
const healthResponse = await apolloClient.query({
|
|
query: gql`
|
|
query Health {
|
|
health
|
|
}
|
|
`,
|
|
});
|
|
expect(healthResponse.data.health).toBeTruthy();
|
|
expect(checked).toBeTruthy();
|
|
});
|
|
|
|
it('should handle Parse headers', async () => {
|
|
let checked = false;
|
|
const originalGetGraphQLOptions = parseGraphQLServer._getGraphQLOptions;
|
|
parseGraphQLServer._getGraphQLOptions = async req => {
|
|
expect(req.info).toBeDefined();
|
|
expect(req.config).toBeDefined();
|
|
expect(req.auth).toBeDefined();
|
|
checked = true;
|
|
return await originalGetGraphQLOptions.bind(parseGraphQLServer)(req);
|
|
};
|
|
const health = (await apolloClient.query({
|
|
query: gql`
|
|
query Health {
|
|
health
|
|
}
|
|
`,
|
|
})).data.health;
|
|
expect(health).toBeTruthy();
|
|
expect(checked).toBeTruthy();
|
|
parseGraphQLServer._getGraphQLOptions = originalGetGraphQLOptions;
|
|
});
|
|
});
|
|
|
|
describe('Playground', () => {
|
|
it('should mount playground', async () => {
|
|
const res = await req({
|
|
method: 'GET',
|
|
url: 'http://localhost:13377/playground',
|
|
});
|
|
expect(res.status).toEqual(200);
|
|
});
|
|
});
|
|
|
|
describe('Schema', () => {
|
|
describe('Default Types', () => {
|
|
it('should have Object scalar type', async () => {
|
|
const objectType = (await apolloClient.query({
|
|
query: gql`
|
|
query ObjectType {
|
|
__type(name: "Object") {
|
|
kind
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(objectType.kind).toEqual('SCALAR');
|
|
});
|
|
|
|
it('should have Date scalar type', async () => {
|
|
const dateType = (await apolloClient.query({
|
|
query: gql`
|
|
query DateType {
|
|
__type(name: "Date") {
|
|
kind
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(dateType.kind).toEqual('SCALAR');
|
|
});
|
|
|
|
it('should have File object type', async () => {
|
|
const fileType = (await apolloClient.query({
|
|
query: gql`
|
|
query FileType {
|
|
__type(name: "FileInfo") {
|
|
kind
|
|
fields {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(fileType.kind).toEqual('OBJECT');
|
|
expect(fileType.fields.map(field => field.name).sort()).toEqual([
|
|
'name',
|
|
'url',
|
|
]);
|
|
});
|
|
|
|
it('should have CreateResult object type', async () => {
|
|
const createResultType = (await apolloClient.query({
|
|
query: gql`
|
|
query CreateResultType {
|
|
__type(name: "CreateResult") {
|
|
kind
|
|
fields {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(createResultType.kind).toEqual('OBJECT');
|
|
expect(
|
|
createResultType.fields.map(field => field.name).sort()
|
|
).toEqual(['createdAt', 'objectId']);
|
|
});
|
|
|
|
it('should have UpdateResult object type', async () => {
|
|
const updateResultType = (await apolloClient.query({
|
|
query: gql`
|
|
query UpdateResultType {
|
|
__type(name: "UpdateResult") {
|
|
kind
|
|
fields {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(updateResultType.kind).toEqual('OBJECT');
|
|
expect(updateResultType.fields.map(field => field.name)).toEqual([
|
|
'updatedAt',
|
|
]);
|
|
});
|
|
|
|
it('should have Class interface type', async () => {
|
|
const classType = (await apolloClient.query({
|
|
query: gql`
|
|
query ClassType {
|
|
__type(name: "Class") {
|
|
kind
|
|
fields {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(classType.kind).toEqual('INTERFACE');
|
|
expect(classType.fields.map(field => field.name).sort()).toEqual([
|
|
'ACL',
|
|
'createdAt',
|
|
'objectId',
|
|
'updatedAt',
|
|
]);
|
|
});
|
|
|
|
it('should have ReadPreference enum type', async () => {
|
|
const readPreferenceType = (await apolloClient.query({
|
|
query: gql`
|
|
query ReadPreferenceType {
|
|
__type(name: "ReadPreference") {
|
|
kind
|
|
enumValues {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(readPreferenceType.kind).toEqual('ENUM');
|
|
expect(
|
|
readPreferenceType.enumValues.map(value => value.name).sort()
|
|
).toEqual([
|
|
'NEAREST',
|
|
'PRIMARY',
|
|
'PRIMARY_PREFERRED',
|
|
'SECONDARY',
|
|
'SECONDARY_PREFERRED',
|
|
]);
|
|
});
|
|
|
|
it('should have FindResult object type', async () => {
|
|
const findResultType = (await apolloClient.query({
|
|
query: gql`
|
|
query FindResultType {
|
|
__type(name: "FindResult") {
|
|
kind
|
|
fields {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(findResultType.kind).toEqual('OBJECT');
|
|
expect(findResultType.fields.map(name => name.name).sort()).toEqual([
|
|
'count',
|
|
'results',
|
|
]);
|
|
});
|
|
|
|
it('should have GraphQLUpload object type', async () => {
|
|
const graphQLUploadType = (await apolloClient.query({
|
|
query: gql`
|
|
query GraphQLUploadType {
|
|
__type(name: "Upload") {
|
|
kind
|
|
fields {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'];
|
|
expect(graphQLUploadType.kind).toEqual('SCALAR');
|
|
});
|
|
|
|
it('should have all expected types', async () => {
|
|
const schemaTypes = (await apolloClient.query({
|
|
query: gql`
|
|
query SchemaTypes {
|
|
__schema {
|
|
types {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__schema'].types.map(type => type.name);
|
|
|
|
const expectedTypes = [
|
|
'Class',
|
|
'CreateResult',
|
|
'Date',
|
|
'File',
|
|
'FilesMutation',
|
|
'FindResult',
|
|
'ObjectsMutation',
|
|
'ObjectsQuery',
|
|
'ReadPreference',
|
|
'UpdateResult',
|
|
'Upload',
|
|
];
|
|
expect(
|
|
expectedTypes.every(type => schemaTypes.indexOf(type) !== -1)
|
|
).toBeTruthy(JSON.stringify(schemaTypes.types));
|
|
});
|
|
});
|
|
|
|
describe('Parse Class Types', () => {
|
|
it('should have all expected types', async () => {
|
|
await parseServer.config.databaseController.loadSchema();
|
|
|
|
const schemaTypes = (await apolloClient.query({
|
|
query: gql`
|
|
query SchemaTypes {
|
|
__schema {
|
|
types {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__schema'].types.map(type => type.name);
|
|
|
|
const expectedTypes = [
|
|
'_RoleClass',
|
|
'_RoleConstraints',
|
|
'_RoleFields',
|
|
'_RoleFindResult',
|
|
'_UserClass',
|
|
'_UserConstraints',
|
|
'_UserFindResult',
|
|
'_UserFields',
|
|
];
|
|
expect(
|
|
expectedTypes.every(type => schemaTypes.indexOf(type) !== -1)
|
|
).toBeTruthy(JSON.stringify(schemaTypes));
|
|
});
|
|
|
|
it('should update schema when it changes', async () => {
|
|
const schemaController = await parseServer.config.databaseController.loadSchema();
|
|
await schemaController.updateClass('_User', {
|
|
foo: { type: 'String' },
|
|
});
|
|
|
|
const userFields = (await apolloClient.query({
|
|
query: gql`
|
|
query UserType {
|
|
__type(name: "_UserClass") {
|
|
fields {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
})).data['__type'].fields.map(field => field.name);
|
|
expect(userFields.indexOf('foo') !== -1).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('Objects Queries', () => {
|
|
describe('Get', () => {
|
|
it('should return a class object using generic query', async () => {
|
|
const obj = new Parse.Object('SomeClass');
|
|
obj.set('someField', 'someValue');
|
|
await obj.save();
|
|
|
|
const result = (await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: obj.id,
|
|
},
|
|
})).data.objects.get;
|
|
|
|
expect(result.objectId).toEqual(obj.id);
|
|
expect(result.someField).toEqual('someValue');
|
|
expect(new Date(result.createdAt)).toEqual(obj.createdAt);
|
|
expect(new Date(result.updatedAt)).toEqual(obj.updatedAt);
|
|
});
|
|
|
|
it('should return a class object using class specific query', async () => {
|
|
const obj = new Parse.Object('Customer');
|
|
obj.set('someField', 'someValue');
|
|
await obj.save();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = (await apolloClient.query({
|
|
query: gql`
|
|
query GetCustomer($objectId: ID!) {
|
|
objects {
|
|
getCustomer(objectId: $objectId) {
|
|
objectId
|
|
someField
|
|
createdAt
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: obj.id,
|
|
},
|
|
})).data.objects.getCustomer;
|
|
|
|
expect(result.objectId).toEqual(obj.id);
|
|
expect(result.someField).toEqual('someValue');
|
|
expect(new Date(result.createdAt)).toEqual(obj.createdAt);
|
|
expect(new Date(result.updatedAt)).toEqual(obj.updatedAt);
|
|
});
|
|
|
|
it('should respect level permissions', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
async function getObject(className, objectId, headers) {
|
|
const specificQueryResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get${className}(objectId: $objectId) {
|
|
objectId
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
|
|
const genericQueryResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($className: String!, $objectId: ID!) {
|
|
objects {
|
|
get(className: $className, objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
className,
|
|
objectId,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
|
|
expect(genericQueryResult.objectId).toEqual(
|
|
specificQueryResult.objectId
|
|
);
|
|
expect(genericQueryResult.createdAt).toEqual(
|
|
specificQueryResult.createdAt
|
|
);
|
|
return genericQueryResult;
|
|
}
|
|
|
|
await Promise.all(
|
|
objects
|
|
.slice(0, 3)
|
|
.map(obj =>
|
|
expectAsync(
|
|
getObject(obj.className, obj.id)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'))
|
|
)
|
|
);
|
|
expect(
|
|
(await getObject(object4.className, object4.id)).data.objects.get
|
|
.someField
|
|
).toEqual('someValue4');
|
|
await Promise.all(
|
|
objects.map(async obj =>
|
|
expect(
|
|
(await getObject(obj.className, obj.id, {
|
|
'X-Parse-Master-Key': 'test',
|
|
})).data.objects.get.someField
|
|
).toEqual(obj.get('someField'))
|
|
)
|
|
);
|
|
await Promise.all(
|
|
objects.map(async obj =>
|
|
expect(
|
|
(await getObject(obj.className, obj.id, {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
})).data.objects.get.someField
|
|
).toEqual(obj.get('someField'))
|
|
)
|
|
);
|
|
await Promise.all(
|
|
objects.map(async obj =>
|
|
expect(
|
|
(await getObject(obj.className, obj.id, {
|
|
'X-Parse-Session-Token': user2.getSessionToken(),
|
|
})).data.objects.get.someField
|
|
).toEqual(obj.get('someField'))
|
|
)
|
|
);
|
|
await expectAsync(
|
|
getObject(object2.className, object2.id, {
|
|
'X-Parse-Session-Token': user3.getSessionToken(),
|
|
})
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await Promise.all(
|
|
[object1, object3, object4].map(async obj =>
|
|
expect(
|
|
(await getObject(obj.className, obj.id, {
|
|
'X-Parse-Session-Token': user3.getSessionToken(),
|
|
})).data.objects.get.someField
|
|
).toEqual(obj.get('someField'))
|
|
)
|
|
);
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(obj =>
|
|
expectAsync(
|
|
getObject(obj.className, obj.id, {
|
|
'X-Parse-Session-Token': user4.getSessionToken(),
|
|
})
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'))
|
|
)
|
|
);
|
|
expect(
|
|
(await getObject(object4.className, object4.id, {
|
|
'X-Parse-Session-Token': user4.getSessionToken(),
|
|
})).data.objects.get.someField
|
|
).toEqual('someValue4');
|
|
await Promise.all(
|
|
objects.slice(0, 2).map(obj =>
|
|
expectAsync(
|
|
getObject(obj.className, obj.id, {
|
|
'X-Parse-Session-Token': user5.getSessionToken(),
|
|
})
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'))
|
|
)
|
|
);
|
|
expect(
|
|
(await getObject(object3.className, object3.id, {
|
|
'X-Parse-Session-Token': user5.getSessionToken(),
|
|
})).data.objects.get.someField
|
|
).toEqual('someValue3');
|
|
expect(
|
|
(await getObject(object4.className, object4.id, {
|
|
'X-Parse-Session-Token': user5.getSessionToken(),
|
|
})).data.objects.get.someField
|
|
).toEqual('someValue4');
|
|
});
|
|
|
|
it('should not bring session token of another user', async () => {
|
|
await prepareData();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_User", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: user2.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
expect(result.data.objects.get.sessionToken).toBeUndefined();
|
|
});
|
|
|
|
it('should not bring session token of current user', async () => {
|
|
await prepareData();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_User", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: user1.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
expect(result.data.objects.get.sessionToken).toBeUndefined();
|
|
});
|
|
|
|
it('should support keys argument', async () => {
|
|
await prepareData();
|
|
|
|
const result1 = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(
|
|
className: "GraphQLClass"
|
|
objectId: $objectId
|
|
keys: "someField"
|
|
)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: object3.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
const result2 = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(
|
|
className: "GraphQLClass"
|
|
objectId: $objectId
|
|
keys: "someField,pointerToUser"
|
|
)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: object3.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result1.data.objects.get.someField).toBeDefined();
|
|
expect(result1.data.objects.get.pointerToUser).toBeUndefined();
|
|
expect(result2.data.objects.get.someField).toBeDefined();
|
|
expect(result2.data.objects.get.pointerToUser).toBeDefined();
|
|
});
|
|
|
|
it('should support include argument', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result1 = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "GraphQLClass", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: object3.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
const result2 = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(
|
|
className: "GraphQLClass"
|
|
objectId: $objectId
|
|
include: "pointerToUser"
|
|
)
|
|
getGraphQLClass(objectId: $objectId) {
|
|
pointerToUser {
|
|
username
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: object3.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(
|
|
result1.data.objects.get.pointerToUser.username
|
|
).toBeUndefined();
|
|
expect(
|
|
result2.data.objects.get.pointerToUser.username
|
|
).toBeDefined();
|
|
expect(
|
|
result2.data.objects.getGraphQLClass.pointerToUser.username
|
|
).toBeDefined();
|
|
});
|
|
|
|
describe_only_db('mongo')('read preferences', () => {
|
|
it('should read from primary by default', async () => {
|
|
await prepareData();
|
|
|
|
const databaseAdapter =
|
|
parseServer.config.databaseController.adapter;
|
|
spyOn(
|
|
databaseAdapter.database.serverConfig,
|
|
'cursor'
|
|
).and.callThrough();
|
|
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(
|
|
className: "GraphQLClass"
|
|
objectId: $objectId
|
|
include: "pointerToUser"
|
|
)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: object3.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
let foundGraphQLClassReadPreference = false;
|
|
let foundUserClassReadPreference = false;
|
|
databaseAdapter.database.serverConfig.cursor.calls
|
|
.all()
|
|
.forEach(call => {
|
|
if (call.args[0].indexOf('GraphQLClass') >= 0) {
|
|
foundGraphQLClassReadPreference = true;
|
|
expect(call.args[2].readPreference).toBe(null);
|
|
} else if (call.args[0].indexOf('_User') >= 0) {
|
|
foundUserClassReadPreference = true;
|
|
expect(call.args[2].readPreference).toBe(null);
|
|
}
|
|
});
|
|
|
|
expect(foundGraphQLClassReadPreference).toBe(true);
|
|
expect(foundUserClassReadPreference).toBe(true);
|
|
});
|
|
|
|
it('should support readPreference argument', async () => {
|
|
await prepareData();
|
|
|
|
const databaseAdapter =
|
|
parseServer.config.databaseController.adapter;
|
|
spyOn(
|
|
databaseAdapter.database.serverConfig,
|
|
'cursor'
|
|
).and.callThrough();
|
|
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(
|
|
className: "GraphQLClass"
|
|
objectId: $objectId
|
|
include: "pointerToUser"
|
|
readPreference: SECONDARY
|
|
)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: object3.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
let foundGraphQLClassReadPreference = false;
|
|
let foundUserClassReadPreference = false;
|
|
databaseAdapter.database.serverConfig.cursor.calls
|
|
.all()
|
|
.forEach(call => {
|
|
if (call.args[0].indexOf('GraphQLClass') >= 0) {
|
|
foundGraphQLClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.SECONDARY
|
|
);
|
|
} else if (call.args[0].indexOf('_User') >= 0) {
|
|
foundUserClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.SECONDARY
|
|
);
|
|
}
|
|
});
|
|
|
|
expect(foundGraphQLClassReadPreference).toBe(true);
|
|
expect(foundUserClassReadPreference).toBe(true);
|
|
});
|
|
|
|
it('should support includeReadPreference argument', async () => {
|
|
await prepareData();
|
|
|
|
const databaseAdapter =
|
|
parseServer.config.databaseController.adapter;
|
|
spyOn(
|
|
databaseAdapter.database.serverConfig,
|
|
'cursor'
|
|
).and.callThrough();
|
|
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(
|
|
className: "GraphQLClass"
|
|
objectId: $objectId
|
|
include: "pointerToUser"
|
|
readPreference: SECONDARY
|
|
includeReadPreference: NEAREST
|
|
)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: object3.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
let foundGraphQLClassReadPreference = false;
|
|
let foundUserClassReadPreference = false;
|
|
databaseAdapter.database.serverConfig.cursor.calls
|
|
.all()
|
|
.forEach(call => {
|
|
if (call.args[0].indexOf('GraphQLClass') >= 0) {
|
|
foundGraphQLClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.SECONDARY
|
|
);
|
|
} else if (call.args[0].indexOf('_User') >= 0) {
|
|
foundUserClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.NEAREST
|
|
);
|
|
}
|
|
});
|
|
|
|
expect(foundGraphQLClassReadPreference).toBe(true);
|
|
expect(foundUserClassReadPreference).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Find', () => {
|
|
it('should return class objects using generic query', async () => {
|
|
const obj1 = new Parse.Object('SomeClass');
|
|
obj1.set('someField', 'someValue1');
|
|
await obj1.save();
|
|
const obj2 = new Parse.Object('SomeClass');
|
|
obj2.set('someField', 'someValue1');
|
|
await obj2.save();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects {
|
|
objects {
|
|
find(className: "SomeClass") {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
expect(result.data.objects.find.results.length).toEqual(2);
|
|
|
|
result.data.objects.find.results.forEach(resultObj => {
|
|
const obj = resultObj.objectId === obj1.id ? obj1 : obj2;
|
|
expect(resultObj.objectId).toEqual(obj.id);
|
|
expect(resultObj.someField).toEqual(obj.get('someField'));
|
|
expect(new Date(resultObj.createdAt)).toEqual(obj.createdAt);
|
|
expect(new Date(resultObj.updatedAt)).toEqual(obj.updatedAt);
|
|
});
|
|
});
|
|
|
|
it('should return class objects using class specific query', async () => {
|
|
const obj1 = new Parse.Object('Customer');
|
|
obj1.set('someField', 'someValue1');
|
|
await obj1.save();
|
|
const obj2 = new Parse.Object('Customer');
|
|
obj2.set('someField', 'someValue1');
|
|
await obj2.save();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindCustomer {
|
|
objects {
|
|
findCustomer {
|
|
results {
|
|
objectId
|
|
someField
|
|
createdAt
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
expect(result.data.objects.findCustomer.results.length).toEqual(2);
|
|
|
|
result.data.objects.findCustomer.results.forEach(resultObj => {
|
|
const obj = resultObj.objectId === obj1.id ? obj1 : obj2;
|
|
expect(resultObj.objectId).toEqual(obj.id);
|
|
expect(resultObj.someField).toEqual(obj.get('someField'));
|
|
expect(new Date(resultObj.createdAt)).toEqual(obj.createdAt);
|
|
expect(new Date(resultObj.updatedAt)).toEqual(obj.updatedAt);
|
|
});
|
|
});
|
|
|
|
it('should respect level permissions', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
async function findObjects(className, headers) {
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects($className: String!) {
|
|
objects {
|
|
find(className: $className) {
|
|
results
|
|
}
|
|
find${className} {
|
|
results {
|
|
objectId
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
className,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
|
|
const genericFindResults = result.data.objects.find.results;
|
|
const specificFindResults =
|
|
result.data.objects[`find${className}`].results;
|
|
genericFindResults.forEach(({ objectId, someField }) => {
|
|
expect(
|
|
specificFindResults.some(
|
|
({
|
|
objectId: specificObjectId,
|
|
someField: specificSomeField,
|
|
}) =>
|
|
objectId === specificObjectId &&
|
|
someField === specificSomeField
|
|
)
|
|
);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
expect(
|
|
(await findObjects('GraphQLClass')).data.objects.find.results.map(
|
|
object => object.someField
|
|
)
|
|
).toEqual([]);
|
|
expect(
|
|
(await findObjects('PublicClass')).data.objects.find.results.map(
|
|
object => object.someField
|
|
)
|
|
).toEqual(['someValue4']);
|
|
expect(
|
|
(await findObjects('GraphQLClass', {
|
|
'X-Parse-Master-Key': 'test',
|
|
})).data.objects.find.results
|
|
.map(object => object.someField)
|
|
.sort()
|
|
).toEqual(['someValue1', 'someValue2', 'someValue3']);
|
|
expect(
|
|
(await findObjects('PublicClass', {
|
|
'X-Parse-Master-Key': 'test',
|
|
})).data.objects.find.results.map(object => object.someField)
|
|
).toEqual(['someValue4']);
|
|
expect(
|
|
(await findObjects('GraphQLClass', {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
})).data.objects.find.results
|
|
.map(object => object.someField)
|
|
.sort()
|
|
).toEqual(['someValue1', 'someValue2', 'someValue3']);
|
|
expect(
|
|
(await findObjects('PublicClass', {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
})).data.objects.find.results.map(object => object.someField)
|
|
).toEqual(['someValue4']);
|
|
expect(
|
|
(await findObjects('GraphQLClass', {
|
|
'X-Parse-Session-Token': user2.getSessionToken(),
|
|
})).data.objects.find.results
|
|
.map(object => object.someField)
|
|
.sort()
|
|
).toEqual(['someValue1', 'someValue2', 'someValue3']);
|
|
expect(
|
|
(await findObjects('GraphQLClass', {
|
|
'X-Parse-Session-Token': user3.getSessionToken(),
|
|
})).data.objects.find.results
|
|
.map(object => object.someField)
|
|
.sort()
|
|
).toEqual(['someValue1', 'someValue3']);
|
|
expect(
|
|
(await findObjects('GraphQLClass', {
|
|
'X-Parse-Session-Token': user4.getSessionToken(),
|
|
})).data.objects.find.results.map(object => object.someField)
|
|
).toEqual([]);
|
|
expect(
|
|
(await findObjects('GraphQLClass', {
|
|
'X-Parse-Session-Token': user5.getSessionToken(),
|
|
})).data.objects.find.results.map(object => object.someField)
|
|
).toEqual(['someValue3']);
|
|
});
|
|
|
|
it('should support where argument using generic query', async () => {
|
|
await prepareData();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects($where: Object) {
|
|
objects {
|
|
find(className: "GraphQLClass", where: $where) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
someField: {
|
|
$in: ['someValue1', 'someValue2', 'someValue3'],
|
|
},
|
|
$or: [
|
|
{
|
|
pointerToUser: {
|
|
__type: 'Pointer',
|
|
className: '_User',
|
|
objectId: user5.id,
|
|
},
|
|
},
|
|
{
|
|
objectId: object1.id,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(
|
|
result.data.objects.find.results
|
|
.map(object => object.someField)
|
|
.sort()
|
|
).toEqual(['someValue1', 'someValue3']);
|
|
});
|
|
|
|
it('should support where argument using class specific query', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects($where: GraphQLClassConstraints) {
|
|
objects {
|
|
findGraphQLClass(where: $where) {
|
|
results {
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
someField: {
|
|
_in: ['someValue1', 'someValue2', 'someValue3'],
|
|
},
|
|
_or: [
|
|
{
|
|
pointerToUser: {
|
|
_eq: {
|
|
__type: 'Pointer',
|
|
className: '_User',
|
|
objectId: user5.id,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
objectId: {
|
|
_eq: object1.id,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(
|
|
result.data.objects.findGraphQLClass.results
|
|
.map(object => object.someField)
|
|
.sort()
|
|
).toEqual(['someValue1', 'someValue3']);
|
|
});
|
|
|
|
it('should support order, skip and limit arguments', async () => {
|
|
const promises = [];
|
|
for (let i = 0; i < 100; i++) {
|
|
const obj = new Parse.Object('SomeClass');
|
|
obj.set('someField', `someValue${i < 10 ? '0' : ''}${i}`);
|
|
obj.set('numberField', i % 3);
|
|
promises.push(obj.save());
|
|
}
|
|
await Promise.all(promises);
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects(
|
|
$className: String!
|
|
$where: Object
|
|
$whereCustom: SomeClassConstraints
|
|
$order: String
|
|
$orderCustom: [SomeClassOrder!]
|
|
$skip: Int
|
|
$limit: Int
|
|
) {
|
|
objects {
|
|
find(
|
|
className: $className
|
|
where: $where
|
|
order: $order
|
|
skip: $skip
|
|
limit: $limit
|
|
) {
|
|
results
|
|
}
|
|
findSomeClass(
|
|
where: $whereCustom
|
|
order: $orderCustom
|
|
skip: $skip
|
|
limit: $limit
|
|
) {
|
|
results {
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
className: 'SomeClass',
|
|
where: {
|
|
someField: {
|
|
$regex: '^someValue',
|
|
},
|
|
},
|
|
whereCustom: {
|
|
someField: {
|
|
_regex: '^someValue',
|
|
},
|
|
},
|
|
order: '-numberField,someField',
|
|
orderCustom: ['numberField_DESC', 'someField_ASC'],
|
|
skip: 4,
|
|
limit: 2,
|
|
},
|
|
});
|
|
|
|
expect(
|
|
result.data.objects.find.results.map(obj => obj.someField)
|
|
).toEqual(['someValue14', 'someValue17']);
|
|
expect(
|
|
result.data.objects.findSomeClass.results.map(
|
|
obj => obj.someField
|
|
)
|
|
).toEqual(['someValue14', 'someValue17']);
|
|
});
|
|
|
|
it('should support count', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const where = {
|
|
someField: {
|
|
_in: ['someValue1', 'someValue2', 'someValue3'],
|
|
},
|
|
_or: [
|
|
{
|
|
pointerToUser: {
|
|
_eq: {
|
|
__type: 'Pointer',
|
|
className: '_User',
|
|
objectId: user5.id,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
objectId: {
|
|
_eq: object1.id,
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects(
|
|
$where1: Object
|
|
$where2: GraphQLClassConstraints
|
|
$limit: Int
|
|
) {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
where: $where1
|
|
limit: $limit
|
|
) {
|
|
results
|
|
count
|
|
}
|
|
findGraphQLClass(where: $where2, limit: $limit) {
|
|
results {
|
|
objectId
|
|
}
|
|
count
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where1: where,
|
|
where2: where,
|
|
limit: 0,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.find.results).toEqual([]);
|
|
expect(result.data.objects.find.count).toEqual(2);
|
|
expect(result.data.objects.findGraphQLClass.results).toEqual([]);
|
|
expect(result.data.objects.findGraphQLClass.count).toEqual(2);
|
|
});
|
|
|
|
it('should only count', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const where = {
|
|
someField: {
|
|
_in: ['someValue1', 'someValue2', 'someValue3'],
|
|
},
|
|
_or: [
|
|
{
|
|
pointerToUser: {
|
|
_eq: {
|
|
__type: 'Pointer',
|
|
className: '_User',
|
|
objectId: user5.id,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
objectId: {
|
|
_eq: object1.id,
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects(
|
|
$where1: Object
|
|
$where2: GraphQLClassConstraints
|
|
) {
|
|
objects {
|
|
find(className: "GraphQLClass", where: $where1) {
|
|
count
|
|
}
|
|
findGraphQLClass(where: $where2) {
|
|
count
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where1: where,
|
|
where2: where,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.find.results).toBeUndefined();
|
|
expect(result.data.objects.find.count).toEqual(2);
|
|
expect(
|
|
result.data.objects.findGraphQLClass.results
|
|
).toBeUndefined();
|
|
expect(result.data.objects.findGraphQLClass.count).toEqual(2);
|
|
});
|
|
|
|
it('should respect max limit', async () => {
|
|
parseServer = await global.reconfigureServer({
|
|
maxLimit: 10,
|
|
});
|
|
|
|
const promises = [];
|
|
for (let i = 0; i < 100; i++) {
|
|
const obj = new Parse.Object('SomeClass');
|
|
promises.push(obj.save());
|
|
}
|
|
await Promise.all(promises);
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects($limit: Int) {
|
|
objects {
|
|
find(
|
|
className: "SomeClass"
|
|
where: { objectId: { _exists: true } }
|
|
limit: $limit
|
|
) {
|
|
results
|
|
count
|
|
}
|
|
findSomeClass(
|
|
where: { objectId: { _exists: true } }
|
|
limit: $limit
|
|
) {
|
|
results {
|
|
objectId
|
|
}
|
|
count
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
limit: 50,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.find.results.length).toEqual(10);
|
|
expect(result.data.objects.find.count).toEqual(100);
|
|
expect(result.data.objects.findSomeClass.results.length).toEqual(
|
|
10
|
|
);
|
|
expect(result.data.objects.findSomeClass.count).toEqual(100);
|
|
});
|
|
|
|
it('should support keys argument', async () => {
|
|
await prepareData();
|
|
|
|
const result1 = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject($where: Object) {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
where: $where
|
|
keys: "someField"
|
|
) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
objectId: object3.id,
|
|
},
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
const result2 = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject($where: Object) {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
where: $where
|
|
keys: "someField,pointerToUser"
|
|
) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
objectId: object3.id,
|
|
},
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(
|
|
result1.data.objects.find.results[0].someField
|
|
).toBeDefined();
|
|
expect(
|
|
result1.data.objects.find.results[0].pointerToUser
|
|
).toBeUndefined();
|
|
expect(
|
|
result2.data.objects.find.results[0].someField
|
|
).toBeDefined();
|
|
expect(
|
|
result2.data.objects.find.results[0].pointerToUser
|
|
).toBeDefined();
|
|
});
|
|
|
|
it('should support include argument', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result1 = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject($where: Object) {
|
|
objects {
|
|
find(className: "GraphQLClass", where: $where) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
objectId: object3.id,
|
|
},
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
const where = {
|
|
objectId: {
|
|
_eq: object3.id,
|
|
},
|
|
};
|
|
|
|
const result2 = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject(
|
|
$where1: Object
|
|
$where2: GraphQLClassConstraints
|
|
) {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
where: $where1
|
|
include: "pointerToUser"
|
|
) {
|
|
results
|
|
}
|
|
findGraphQLClass(where: $where2) {
|
|
results {
|
|
pointerToUser {
|
|
username
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where1: where,
|
|
where2: where,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(
|
|
result1.data.objects.find.results[0].pointerToUser.username
|
|
).toBeUndefined();
|
|
expect(
|
|
result2.data.objects.find.results[0].pointerToUser.username
|
|
).toBeDefined();
|
|
expect(
|
|
result2.data.objects.findGraphQLClass.results[0].pointerToUser
|
|
.username
|
|
).toBeDefined();
|
|
});
|
|
|
|
it('should support includeAll argument', async () => {
|
|
const obj1 = new Parse.Object('SomeClass1');
|
|
obj1.set('someField1', 'someValue1');
|
|
const obj2 = new Parse.Object('SomeClass2');
|
|
obj2.set('someField2', 'someValue2');
|
|
const obj3 = new Parse.Object('SomeClass3');
|
|
obj3.set('obj1', obj1);
|
|
obj3.set('obj2', obj2);
|
|
await Promise.all([obj1.save(), obj2.save(), obj3.save()]);
|
|
|
|
const result1 = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject {
|
|
objects {
|
|
find(className: "SomeClass3") {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
const result2 = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject {
|
|
objects {
|
|
find(className: "SomeClass3", includeAll: true) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
expect(
|
|
result1.data.objects.find.results[0].obj1.someField1
|
|
).toBeUndefined();
|
|
expect(
|
|
result1.data.objects.find.results[0].obj2.someField2
|
|
).toBeUndefined();
|
|
expect(
|
|
result2.data.objects.find.results[0].obj1.someField1
|
|
).toEqual('someValue1');
|
|
expect(
|
|
result2.data.objects.find.results[0].obj2.someField2
|
|
).toEqual('someValue2');
|
|
});
|
|
|
|
describe_only_db('mongo')('read preferences', () => {
|
|
it('should read from primary by default', async () => {
|
|
await prepareData();
|
|
|
|
const databaseAdapter =
|
|
parseServer.config.databaseController.adapter;
|
|
spyOn(
|
|
databaseAdapter.database.serverConfig,
|
|
'cursor'
|
|
).and.callThrough();
|
|
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
include: "pointerToUser"
|
|
) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
let foundGraphQLClassReadPreference = false;
|
|
let foundUserClassReadPreference = false;
|
|
databaseAdapter.database.serverConfig.cursor.calls
|
|
.all()
|
|
.forEach(call => {
|
|
if (call.args[0].indexOf('GraphQLClass') >= 0) {
|
|
foundGraphQLClassReadPreference = true;
|
|
expect(call.args[2].readPreference).toBe(null);
|
|
} else if (call.args[0].indexOf('_User') >= 0) {
|
|
foundUserClassReadPreference = true;
|
|
expect(call.args[2].readPreference).toBe(null);
|
|
}
|
|
});
|
|
|
|
expect(foundGraphQLClassReadPreference).toBe(true);
|
|
expect(foundUserClassReadPreference).toBe(true);
|
|
});
|
|
|
|
it('should support readPreference argument', async () => {
|
|
await prepareData();
|
|
|
|
const databaseAdapter =
|
|
parseServer.config.databaseController.adapter;
|
|
spyOn(
|
|
databaseAdapter.database.serverConfig,
|
|
'cursor'
|
|
).and.callThrough();
|
|
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
include: "pointerToUser"
|
|
readPreference: SECONDARY
|
|
) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
let foundGraphQLClassReadPreference = false;
|
|
let foundUserClassReadPreference = false;
|
|
databaseAdapter.database.serverConfig.cursor.calls
|
|
.all()
|
|
.forEach(call => {
|
|
if (call.args[0].indexOf('GraphQLClass') >= 0) {
|
|
foundGraphQLClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.SECONDARY
|
|
);
|
|
} else if (call.args[0].indexOf('_User') >= 0) {
|
|
foundUserClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.SECONDARY
|
|
);
|
|
}
|
|
});
|
|
|
|
expect(foundGraphQLClassReadPreference).toBe(true);
|
|
expect(foundUserClassReadPreference).toBe(true);
|
|
});
|
|
|
|
it('should support includeReadPreference argument', async () => {
|
|
await prepareData();
|
|
|
|
const databaseAdapter =
|
|
parseServer.config.databaseController.adapter;
|
|
spyOn(
|
|
databaseAdapter.database.serverConfig,
|
|
'cursor'
|
|
).and.callThrough();
|
|
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
include: "pointerToUser"
|
|
readPreference: SECONDARY
|
|
includeReadPreference: NEAREST
|
|
) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
let foundGraphQLClassReadPreference = false;
|
|
let foundUserClassReadPreference = false;
|
|
databaseAdapter.database.serverConfig.cursor.calls
|
|
.all()
|
|
.forEach(call => {
|
|
if (call.args[0].indexOf('GraphQLClass') >= 0) {
|
|
foundGraphQLClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.SECONDARY
|
|
);
|
|
} else if (call.args[0].indexOf('_User') >= 0) {
|
|
foundUserClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.NEAREST
|
|
);
|
|
}
|
|
});
|
|
|
|
expect(foundGraphQLClassReadPreference).toBe(true);
|
|
expect(foundUserClassReadPreference).toBe(true);
|
|
});
|
|
|
|
it('should support subqueryReadPreference argument', async () => {
|
|
await prepareData();
|
|
|
|
const databaseAdapter =
|
|
parseServer.config.databaseController.adapter;
|
|
spyOn(
|
|
databaseAdapter.database.serverConfig,
|
|
'cursor'
|
|
).and.callThrough();
|
|
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects($where: Object) {
|
|
objects {
|
|
find(
|
|
className: "GraphQLClass"
|
|
where: $where
|
|
readPreference: SECONDARY
|
|
subqueryReadPreference: NEAREST
|
|
) {
|
|
count
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
pointerToUser: {
|
|
$inQuery: { where: {}, className: '_User' },
|
|
},
|
|
},
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
let foundGraphQLClassReadPreference = false;
|
|
let foundUserClassReadPreference = false;
|
|
databaseAdapter.database.serverConfig.cursor.calls
|
|
.all()
|
|
.forEach(call => {
|
|
if (call.args[0].indexOf('GraphQLClass') >= 0) {
|
|
foundGraphQLClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.SECONDARY
|
|
);
|
|
} else if (call.args[0].indexOf('_User') >= 0) {
|
|
foundUserClassReadPreference = true;
|
|
expect(call.args[2].readPreference.preference).toBe(
|
|
ReadPreference.NEAREST
|
|
);
|
|
}
|
|
});
|
|
|
|
expect(foundGraphQLClassReadPreference).toBe(true);
|
|
expect(foundUserClassReadPreference).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Objects Mutations', () => {
|
|
describe('Create', () => {
|
|
it('should return CreateResult object using generic mutation', async () => {
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: 'someValue',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.create.objectId).toBeDefined();
|
|
|
|
const obj = await new Parse.Query('SomeClass').get(
|
|
result.data.objects.create.objectId
|
|
);
|
|
|
|
expect(obj.createdAt).toEqual(
|
|
new Date(result.data.objects.create.createdAt)
|
|
);
|
|
expect(obj.get('someField')).toEqual('someValue');
|
|
});
|
|
|
|
it('should return CreateResult object using class specific mutation', async () => {
|
|
const customerSchema = new Parse.Schema('Customer');
|
|
customerSchema.addString('someField');
|
|
await customerSchema.save();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateCustomer($fields: CustomerFields) {
|
|
objects {
|
|
createCustomer(fields: $fields) {
|
|
objectId
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: 'someValue',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.createCustomer.objectId).toBeDefined();
|
|
|
|
const customer = await new Parse.Query('Customer').get(
|
|
result.data.objects.createCustomer.objectId
|
|
);
|
|
|
|
expect(customer.createdAt).toEqual(
|
|
new Date(result.data.objects.createCustomer.createdAt)
|
|
);
|
|
expect(customer.get('someField')).toEqual('someValue');
|
|
});
|
|
|
|
it('should respect level permissions', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
async function createObject(className, headers) {
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($className: String!) {
|
|
objects {
|
|
create(className: $className) {
|
|
objectId
|
|
createdAt
|
|
}
|
|
create${className} {
|
|
objectId
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
className,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
|
|
const { create } = result.data.objects;
|
|
expect(create.objectId).toBeDefined();
|
|
expect(create.createdAt).toBeDefined();
|
|
|
|
const specificCreate = result.data.objects[`create${className}`];
|
|
expect(specificCreate.objectId).toBeDefined();
|
|
expect(specificCreate.createdAt).toBeDefined();
|
|
|
|
return result;
|
|
}
|
|
|
|
await expectAsync(createObject('GraphQLClass')).toBeRejectedWith(
|
|
jasmine.stringMatching(
|
|
'Permission denied for action create on class GraphQLClass'
|
|
)
|
|
);
|
|
await expectAsync(createObject('PublicClass')).toBeResolved();
|
|
await expectAsync(
|
|
createObject('GraphQLClass', { 'X-Parse-Master-Key': 'test' })
|
|
).toBeResolved();
|
|
await expectAsync(
|
|
createObject('PublicClass', { 'X-Parse-Master-Key': 'test' })
|
|
).toBeResolved();
|
|
await expectAsync(
|
|
createObject('GraphQLClass', {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
})
|
|
).toBeResolved();
|
|
await expectAsync(
|
|
createObject('PublicClass', {
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
})
|
|
).toBeResolved();
|
|
await expectAsync(
|
|
createObject('GraphQLClass', {
|
|
'X-Parse-Session-Token': user2.getSessionToken(),
|
|
})
|
|
).toBeResolved();
|
|
await expectAsync(
|
|
createObject('PublicClass', {
|
|
'X-Parse-Session-Token': user2.getSessionToken(),
|
|
})
|
|
).toBeResolved();
|
|
await expectAsync(
|
|
createObject('GraphQLClass', {
|
|
'X-Parse-Session-Token': user4.getSessionToken(),
|
|
})
|
|
).toBeRejectedWith(
|
|
jasmine.stringMatching(
|
|
'Permission denied for action create on class GraphQLClass'
|
|
)
|
|
);
|
|
await expectAsync(
|
|
createObject('PublicClass', {
|
|
'X-Parse-Session-Token': user4.getSessionToken(),
|
|
})
|
|
).toBeResolved();
|
|
});
|
|
});
|
|
|
|
describe('Update', () => {
|
|
it('should return UpdateResult object using generic mutation', async () => {
|
|
const obj = new Parse.Object('SomeClass');
|
|
obj.set('someField1', 'someField1Value1');
|
|
obj.set('someField2', 'someField2Value1');
|
|
await obj.save();
|
|
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation UpdateSomeObject($objectId: ID!, $fields: Object) {
|
|
objects {
|
|
update(
|
|
className: "SomeClass"
|
|
objectId: $objectId
|
|
fields: $fields
|
|
) {
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: obj.id,
|
|
fields: {
|
|
someField1: 'someField1Value2',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.update.updatedAt).toBeDefined();
|
|
|
|
await obj.fetch();
|
|
|
|
expect(obj.get('someField1')).toEqual('someField1Value2');
|
|
expect(obj.get('someField2')).toEqual('someField2Value1');
|
|
});
|
|
|
|
it('should return UpdateResult object using class specific mutation', async () => {
|
|
const obj = new Parse.Object('Customer');
|
|
obj.set('someField1', 'someField1Value1');
|
|
obj.set('someField2', 'someField2Value1');
|
|
await obj.save();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation UpdateCustomer(
|
|
$objectId: ID!
|
|
$fields: CustomerFields
|
|
) {
|
|
objects {
|
|
updateCustomer(objectId: $objectId, fields: $fields) {
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: obj.id,
|
|
fields: {
|
|
someField1: 'someField1Value2',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.updateCustomer.updatedAt).toBeDefined();
|
|
|
|
await obj.fetch();
|
|
|
|
expect(obj.get('someField1')).toEqual('someField1Value2');
|
|
expect(obj.get('someField2')).toEqual('someField2Value1');
|
|
});
|
|
|
|
it('should respect level permissions', async () => {
|
|
await prepareData();
|
|
|
|
function updateObject(className, objectId, fields, headers) {
|
|
return apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation UpdateSomeObject(
|
|
$className: String!
|
|
$objectId: ID!
|
|
$fields: Object
|
|
) {
|
|
objects {
|
|
update(
|
|
className: $className
|
|
objectId: $objectId
|
|
fields: $fields
|
|
) {
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
className,
|
|
objectId,
|
|
fields,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
}
|
|
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
updateObject(obj.className, obj.id, {
|
|
someField: 'changedValue1',
|
|
})
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await updateObject(object4.className, object4.id, {
|
|
someField: 'changedValue1',
|
|
})).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await object4.fetch({ useMasterKey: true });
|
|
expect(object4.get('someField')).toEqual('changedValue1');
|
|
await Promise.all(
|
|
objects.map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue2' },
|
|
{ 'X-Parse-Master-Key': 'test' }
|
|
)).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue2');
|
|
})
|
|
);
|
|
await Promise.all(
|
|
objects.map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue3' },
|
|
{ 'X-Parse-Session-Token': user1.getSessionToken() }
|
|
)).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue3');
|
|
})
|
|
);
|
|
await Promise.all(
|
|
objects.map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue4' },
|
|
{ 'X-Parse-Session-Token': user2.getSessionToken() }
|
|
)).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue4');
|
|
})
|
|
);
|
|
await Promise.all(
|
|
[object1, object3, object4].map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue5' },
|
|
{ 'X-Parse-Session-Token': user3.getSessionToken() }
|
|
)).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue5');
|
|
})
|
|
);
|
|
const originalFieldValue = object2.get('someField');
|
|
await expectAsync(
|
|
updateObject(
|
|
object2.className,
|
|
object2.id,
|
|
{ someField: 'changedValue5' },
|
|
{ 'X-Parse-Session-Token': user3.getSessionToken() }
|
|
)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await object2.fetch({ useMasterKey: true });
|
|
expect(object2.get('someField')).toEqual(originalFieldValue);
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue6' },
|
|
{ 'X-Parse-Session-Token': user4.getSessionToken() }
|
|
)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await updateObject(
|
|
object4.className,
|
|
object4.id,
|
|
{ someField: 'changedValue6' },
|
|
{ 'X-Parse-Session-Token': user4.getSessionToken() }
|
|
)).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await object4.fetch({ useMasterKey: true });
|
|
expect(object4.get('someField')).toEqual('changedValue6');
|
|
await Promise.all(
|
|
objects.slice(0, 2).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue7' },
|
|
{ 'X-Parse-Session-Token': user5.getSessionToken() }
|
|
)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await updateObject(
|
|
object3.className,
|
|
object3.id,
|
|
{ someField: 'changedValue7' },
|
|
{ 'X-Parse-Session-Token': user5.getSessionToken() }
|
|
)).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await object3.fetch({ useMasterKey: true });
|
|
expect(object3.get('someField')).toEqual('changedValue7');
|
|
expect(
|
|
(await updateObject(
|
|
object4.className,
|
|
object4.id,
|
|
{ someField: 'changedValue7' },
|
|
{ 'X-Parse-Session-Token': user5.getSessionToken() }
|
|
)).data.objects.update.updatedAt
|
|
).toBeDefined();
|
|
await object4.fetch({ useMasterKey: true });
|
|
expect(object4.get('someField')).toEqual('changedValue7');
|
|
});
|
|
|
|
it('should respect level permissions with specific class mutation', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
function updateObject(className, objectId, fields, headers) {
|
|
return apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation UpdateSomeObject(
|
|
$objectId: ID!
|
|
$fields: ${className}Fields
|
|
) {
|
|
objects {
|
|
update${className}(
|
|
objectId: $objectId
|
|
fields: $fields
|
|
) {
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId,
|
|
fields,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
}
|
|
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
updateObject(obj.className, obj.id, {
|
|
someField: 'changedValue1',
|
|
})
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await updateObject(object4.className, object4.id, {
|
|
someField: 'changedValue1',
|
|
})).data.objects[`update${object4.className}`].updatedAt
|
|
).toBeDefined();
|
|
await object4.fetch({ useMasterKey: true });
|
|
expect(object4.get('someField')).toEqual('changedValue1');
|
|
await Promise.all(
|
|
objects.map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue2' },
|
|
{ 'X-Parse-Master-Key': 'test' }
|
|
)).data.objects[`update${obj.className}`].updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue2');
|
|
})
|
|
);
|
|
await Promise.all(
|
|
objects.map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue3' },
|
|
{ 'X-Parse-Session-Token': user1.getSessionToken() }
|
|
)).data.objects[`update${obj.className}`].updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue3');
|
|
})
|
|
);
|
|
await Promise.all(
|
|
objects.map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue4' },
|
|
{ 'X-Parse-Session-Token': user2.getSessionToken() }
|
|
)).data.objects[`update${obj.className}`].updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue4');
|
|
})
|
|
);
|
|
await Promise.all(
|
|
[object1, object3, object4].map(async obj => {
|
|
expect(
|
|
(await updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue5' },
|
|
{ 'X-Parse-Session-Token': user3.getSessionToken() }
|
|
)).data.objects[`update${obj.className}`].updatedAt
|
|
).toBeDefined();
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual('changedValue5');
|
|
})
|
|
);
|
|
const originalFieldValue = object2.get('someField');
|
|
await expectAsync(
|
|
updateObject(
|
|
object2.className,
|
|
object2.id,
|
|
{ someField: 'changedValue5' },
|
|
{ 'X-Parse-Session-Token': user3.getSessionToken() }
|
|
)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await object2.fetch({ useMasterKey: true });
|
|
expect(object2.get('someField')).toEqual(originalFieldValue);
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue6' },
|
|
{ 'X-Parse-Session-Token': user4.getSessionToken() }
|
|
)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await updateObject(
|
|
object4.className,
|
|
object4.id,
|
|
{ someField: 'changedValue6' },
|
|
{ 'X-Parse-Session-Token': user4.getSessionToken() }
|
|
)).data.objects[`update${object4.className}`].updatedAt
|
|
).toBeDefined();
|
|
await object4.fetch({ useMasterKey: true });
|
|
expect(object4.get('someField')).toEqual('changedValue6');
|
|
await Promise.all(
|
|
objects.slice(0, 2).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
updateObject(
|
|
obj.className,
|
|
obj.id,
|
|
{ someField: 'changedValue7' },
|
|
{ 'X-Parse-Session-Token': user5.getSessionToken() }
|
|
)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await updateObject(
|
|
object3.className,
|
|
object3.id,
|
|
{ someField: 'changedValue7' },
|
|
{ 'X-Parse-Session-Token': user5.getSessionToken() }
|
|
)).data.objects[`update${object3.className}`].updatedAt
|
|
).toBeDefined();
|
|
await object3.fetch({ useMasterKey: true });
|
|
expect(object3.get('someField')).toEqual('changedValue7');
|
|
expect(
|
|
(await updateObject(
|
|
object4.className,
|
|
object4.id,
|
|
{ someField: 'changedValue7' },
|
|
{ 'X-Parse-Session-Token': user5.getSessionToken() }
|
|
)).data.objects[`update${object4.className}`].updatedAt
|
|
).toBeDefined();
|
|
await object4.fetch({ useMasterKey: true });
|
|
expect(object4.get('someField')).toEqual('changedValue7');
|
|
});
|
|
});
|
|
|
|
describe('Delete', () => {
|
|
it('should return a boolean confirmation using generic mutation', async () => {
|
|
const obj = new Parse.Object('SomeClass');
|
|
await obj.save();
|
|
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation DeleteSomeObject($objectId: ID!) {
|
|
objects {
|
|
delete(className: "SomeClass", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: obj.id,
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.delete).toEqual(true);
|
|
|
|
await expectAsync(
|
|
obj.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
});
|
|
|
|
it('should return a boolean confirmation using class specific mutation', async () => {
|
|
const obj = new Parse.Object('Customer');
|
|
await obj.save();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation DeleteCustomer($objectId: ID!) {
|
|
objects {
|
|
deleteCustomer(objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: obj.id,
|
|
},
|
|
});
|
|
|
|
expect(result.data.objects.deleteCustomer).toEqual(true);
|
|
|
|
await expectAsync(
|
|
obj.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
});
|
|
|
|
it('should respect level permissions', async () => {
|
|
await prepareData();
|
|
|
|
function deleteObject(className, objectId, headers) {
|
|
return apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation DeleteSomeObject(
|
|
$className: String!
|
|
$objectId: ID!
|
|
) {
|
|
objects {
|
|
delete(className: $className, objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
className,
|
|
objectId,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
}
|
|
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
deleteObject(obj.className, obj.id)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
deleteObject(obj.className, obj.id, {
|
|
'X-Parse-Session-Token': user4.getSessionToken(),
|
|
})
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await deleteObject(object4.className, object4.id)).data.objects
|
|
.delete
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object4.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
expect(
|
|
(await deleteObject(object1.className, object1.id, {
|
|
'X-Parse-Master-Key': 'test',
|
|
})).data.objects.delete
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object1.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
expect(
|
|
(await deleteObject(object2.className, object2.id, {
|
|
'X-Parse-Session-Token': user2.getSessionToken(),
|
|
})).data.objects.delete
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object2.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
expect(
|
|
(await deleteObject(object3.className, object3.id, {
|
|
'X-Parse-Session-Token': user5.getSessionToken(),
|
|
})).data.objects.delete
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object3.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
});
|
|
|
|
it('should respect level permissions with specific class mutation', async () => {
|
|
await prepareData();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
function deleteObject(className, objectId, headers) {
|
|
return apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation DeleteSomeObject(
|
|
$objectId: ID!
|
|
) {
|
|
objects {
|
|
delete${className}(objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId,
|
|
},
|
|
context: {
|
|
headers,
|
|
},
|
|
});
|
|
}
|
|
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
deleteObject(obj.className, obj.id)
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
await Promise.all(
|
|
objects.slice(0, 3).map(async obj => {
|
|
const originalFieldValue = obj.get('someField');
|
|
await expectAsync(
|
|
deleteObject(obj.className, obj.id, {
|
|
'X-Parse-Session-Token': user4.getSessionToken(),
|
|
})
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
await obj.fetch({ useMasterKey: true });
|
|
expect(obj.get('someField')).toEqual(originalFieldValue);
|
|
})
|
|
);
|
|
expect(
|
|
(await deleteObject(object4.className, object4.id)).data.objects[
|
|
`delete${object4.className}`
|
|
]
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object4.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
expect(
|
|
(await deleteObject(object1.className, object1.id, {
|
|
'X-Parse-Master-Key': 'test',
|
|
})).data.objects[`delete${object1.className}`]
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object1.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
expect(
|
|
(await deleteObject(object2.className, object2.id, {
|
|
'X-Parse-Session-Token': user2.getSessionToken(),
|
|
})).data.objects[`delete${object2.className}`]
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object2.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
expect(
|
|
(await deleteObject(object3.className, object3.id, {
|
|
'X-Parse-Session-Token': user5.getSessionToken(),
|
|
})).data.objects[`delete${object3.className}`]
|
|
).toEqual(true);
|
|
await expectAsync(
|
|
object3.fetch({ useMasterKey: true })
|
|
).toBeRejectedWith(jasmine.stringMatching('Object not found'));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Files Mutations', () => {
|
|
describe('Create', () => {
|
|
it('should return File object', async () => {
|
|
parseServer = await global.reconfigureServer({
|
|
publicServerURL: 'http://localhost:13377/parse',
|
|
});
|
|
|
|
const body = new FormData();
|
|
body.append(
|
|
'operations',
|
|
JSON.stringify({
|
|
query: `
|
|
mutation CreateFile($file: Upload!) {
|
|
files {
|
|
create(file: $file) {
|
|
name
|
|
url
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
file: null,
|
|
},
|
|
})
|
|
);
|
|
body.append('map', JSON.stringify({ 1: ['variables.file'] }));
|
|
body.append('1', 'My File Content', {
|
|
filename: 'myFileName.txt',
|
|
contentType: 'text/plain',
|
|
});
|
|
|
|
let res = await fetch('http://localhost:13377/graphql', {
|
|
method: 'POST',
|
|
headers,
|
|
body,
|
|
});
|
|
|
|
expect(res.status).toEqual(200);
|
|
|
|
const result = JSON.parse(await res.text());
|
|
|
|
expect(result.data.files.create.name).toEqual(
|
|
jasmine.stringMatching(/_myFileName.txt$/)
|
|
);
|
|
expect(result.data.files.create.url).toEqual(
|
|
jasmine.stringMatching(/_myFileName.txt$/)
|
|
);
|
|
|
|
res = await fetch(result.data.files.create.url);
|
|
|
|
expect(res.status).toEqual(200);
|
|
expect(await res.text()).toEqual('My File Content');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Users Queries', () => {
|
|
it('should return current logged user', async () => {
|
|
const userName = 'user1',
|
|
password = 'user1',
|
|
email = 'emailUser1@parse.com';
|
|
|
|
const user = new Parse.User();
|
|
user.setUsername(userName);
|
|
user.setPassword(password);
|
|
user.setEmail(email);
|
|
await user.signUp();
|
|
|
|
const session = await Parse.Session.current();
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query GetCurrentUser {
|
|
users {
|
|
me {
|
|
objectId
|
|
username
|
|
email
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': session.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
const {
|
|
objectId,
|
|
username: resultUserName,
|
|
email: resultEmail,
|
|
} = result.data.users.me;
|
|
expect(objectId).toBeDefined();
|
|
expect(resultUserName).toEqual(userName);
|
|
expect(resultEmail).toEqual(email);
|
|
});
|
|
|
|
it('should return logged user including pointer', async () => {
|
|
const foo = new Parse.Object('Foo');
|
|
foo.set('bar', 'hello');
|
|
|
|
const userName = 'user1',
|
|
password = 'user1',
|
|
email = 'emailUser1@parse.com';
|
|
|
|
const user = new Parse.User();
|
|
user.setUsername(userName);
|
|
user.setPassword(password);
|
|
user.setEmail(email);
|
|
user.set('userFoo', foo);
|
|
await user.signUp();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const session = await Parse.Session.current();
|
|
const result = await apolloClient.query({
|
|
query: gql`
|
|
query GetCurrentUser {
|
|
users {
|
|
me {
|
|
objectId
|
|
sessionToken
|
|
userFoo {
|
|
bar
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': session.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
const {
|
|
objectId,
|
|
sessionToken,
|
|
userFoo: resultFoo,
|
|
} = result.data.users.me;
|
|
expect(objectId).toEqual(user.id);
|
|
expect(sessionToken).toBeDefined();
|
|
expect(resultFoo).toBeDefined();
|
|
expect(resultFoo.bar).toEqual('hello');
|
|
});
|
|
});
|
|
|
|
describe('Users Mutations', () => {
|
|
it('should sign user up', async () => {
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation SignUp($fields: _UserSignUpFields) {
|
|
users {
|
|
signUp(fields: $fields) {
|
|
sessionToken
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
username: 'user1',
|
|
password: 'user1',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.data.users.signUp.sessionToken).toBeDefined();
|
|
expect(typeof result.data.users.signUp.sessionToken).toBe('string');
|
|
});
|
|
|
|
it('should log the user in', async () => {
|
|
const user = new Parse.User();
|
|
user.setUsername('user1');
|
|
user.setPassword('user1');
|
|
await user.signUp();
|
|
await Parse.User.logOut();
|
|
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation LogInUser($username: String!, $password: String!) {
|
|
users {
|
|
logIn(username: $username, password: $password) {
|
|
sessionToken
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
username: 'user1',
|
|
password: 'user1',
|
|
},
|
|
});
|
|
|
|
expect(result.data.users.logIn.sessionToken).toBeDefined();
|
|
expect(typeof result.data.users.logIn.sessionToken).toBe('string');
|
|
});
|
|
|
|
it('should log the user out', async () => {
|
|
const user = new Parse.User();
|
|
user.setUsername('user1');
|
|
user.setPassword('user1');
|
|
await user.signUp();
|
|
await Parse.User.logOut();
|
|
|
|
const logIn = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation LogInUser($username: String!, $password: String!) {
|
|
users {
|
|
logIn(username: $username, password: $password) {
|
|
sessionToken
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
username: 'user1',
|
|
password: 'user1',
|
|
},
|
|
});
|
|
|
|
const sessionToken = logIn.data.users.logIn.sessionToken;
|
|
|
|
const logOut = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation LogOutUser {
|
|
users {
|
|
logOut
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': sessionToken,
|
|
},
|
|
},
|
|
});
|
|
expect(logOut.data.users.logOut).toBeTruthy();
|
|
|
|
try {
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query GetCurrentUser {
|
|
users {
|
|
me {
|
|
username
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': sessionToken,
|
|
},
|
|
},
|
|
});
|
|
fail('should not retrieve current user due to session token');
|
|
} catch (err) {
|
|
const { statusCode, result } = err.networkError;
|
|
expect(statusCode).toBe(400);
|
|
expect(result).toEqual({
|
|
code: 209,
|
|
error: 'Invalid session token',
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Session Token', () => {
|
|
it('should fail due to invalid session token', async () => {
|
|
try {
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query GetCurrentUser {
|
|
users {
|
|
me {
|
|
username
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': 'foo',
|
|
},
|
|
},
|
|
});
|
|
fail('should not retrieve current user due to session token');
|
|
} catch (err) {
|
|
const { statusCode, result } = err.networkError;
|
|
expect(statusCode).toBe(400);
|
|
expect(result).toEqual({
|
|
code: 209,
|
|
error: 'Invalid session token',
|
|
});
|
|
}
|
|
});
|
|
|
|
it('should fail due to empty session token', async () => {
|
|
try {
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query GetCurrentUser {
|
|
users {
|
|
me {
|
|
username
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': '',
|
|
},
|
|
},
|
|
});
|
|
fail('should not retrieve current user due to session token');
|
|
} catch (err) {
|
|
const { graphQLErrors } = err;
|
|
expect(graphQLErrors.length).toBe(1);
|
|
expect(graphQLErrors[0].message).toBe('Invalid session token');
|
|
}
|
|
});
|
|
|
|
it('should find a user and fail due to empty session token', async () => {
|
|
const car = new Parse.Object('Car');
|
|
await car.save();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
try {
|
|
await apolloClient.query({
|
|
query: gql`
|
|
query GetCurrentUser {
|
|
users {
|
|
me {
|
|
username
|
|
}
|
|
}
|
|
objects {
|
|
findCar {
|
|
results {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': '',
|
|
},
|
|
},
|
|
});
|
|
fail('should not retrieve current user due to session token');
|
|
} catch (err) {
|
|
const { graphQLErrors } = err;
|
|
expect(graphQLErrors.length).toBe(1);
|
|
expect(graphQLErrors[0].message).toBe('Invalid session token');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Functions Mutations', () => {
|
|
it('can be called', async () => {
|
|
Parse.Cloud.define('hello', async () => {
|
|
return 'Hello world!';
|
|
});
|
|
|
|
const result = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CallFunction {
|
|
functions {
|
|
call(functionName: "hello")
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
expect(result.data.functions.call).toEqual('Hello world!');
|
|
});
|
|
|
|
it('can throw errors', async () => {
|
|
Parse.Cloud.define('hello', async () => {
|
|
throw new Error('Some error message.');
|
|
});
|
|
|
|
try {
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CallFunction {
|
|
functions {
|
|
call(functionName: "hello")
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
fail('Should throw an error');
|
|
} catch (e) {
|
|
const { graphQLErrors } = e;
|
|
expect(graphQLErrors.length).toBe(1);
|
|
expect(graphQLErrors[0].message).toBe('Some error message.');
|
|
}
|
|
});
|
|
|
|
it('should accept different params', done => {
|
|
Parse.Cloud.define('hello', async req => {
|
|
expect(req.params.date instanceof Date).toBe(true);
|
|
expect(req.params.date.getTime()).toBe(1463907600000);
|
|
expect(req.params.dateList[0] instanceof Date).toBe(true);
|
|
expect(req.params.dateList[0].getTime()).toBe(1463907600000);
|
|
expect(req.params.complexStructure.date[0] instanceof Date).toBe(
|
|
true
|
|
);
|
|
expect(req.params.complexStructure.date[0].getTime()).toBe(
|
|
1463907600000
|
|
);
|
|
expect(
|
|
req.params.complexStructure.deepDate.date[0] instanceof Date
|
|
).toBe(true);
|
|
expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(
|
|
1463907600000
|
|
);
|
|
expect(
|
|
req.params.complexStructure.deepDate2[0].date instanceof Date
|
|
).toBe(true);
|
|
expect(
|
|
req.params.complexStructure.deepDate2[0].date.getTime()
|
|
).toBe(1463907600000);
|
|
// Regression for #2294
|
|
expect(req.params.file instanceof Parse.File).toBe(true);
|
|
expect(req.params.file.url()).toEqual('https://some.url');
|
|
// Regression for #2204
|
|
expect(req.params.array).toEqual(['a', 'b', 'c']);
|
|
expect(Array.isArray(req.params.array)).toBe(true);
|
|
expect(req.params.arrayOfArray).toEqual([
|
|
['a', 'b', 'c'],
|
|
['d', 'e', 'f'],
|
|
]);
|
|
expect(Array.isArray(req.params.arrayOfArray)).toBe(true);
|
|
expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true);
|
|
expect(Array.isArray(req.params.arrayOfArray[1])).toBe(true);
|
|
|
|
done();
|
|
});
|
|
|
|
const params = {
|
|
date: {
|
|
__type: 'Date',
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
},
|
|
dateList: [
|
|
{
|
|
__type: 'Date',
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
},
|
|
],
|
|
lol: 'hello',
|
|
complexStructure: {
|
|
date: [
|
|
{
|
|
__type: 'Date',
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
},
|
|
],
|
|
deepDate: {
|
|
date: [
|
|
{
|
|
__type: 'Date',
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
},
|
|
],
|
|
},
|
|
deepDate2: [
|
|
{
|
|
date: {
|
|
__type: 'Date',
|
|
iso: '2016-05-22T09:00:00.000Z',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
file: Parse.File.fromJSON({
|
|
__type: 'File',
|
|
name: 'name',
|
|
url: 'https://some.url',
|
|
}),
|
|
array: ['a', 'b', 'c'],
|
|
arrayOfArray: [['a', 'b', 'c'], ['d', 'e', 'f']],
|
|
};
|
|
|
|
apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CallFunction($params: Object) {
|
|
functions {
|
|
call(functionName: "hello", params: $params)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
params,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Data Types', () => {
|
|
it('should support String', async () => {
|
|
const someFieldValue = 'some string';
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('String');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!, $someFieldValue: String) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(
|
|
where: { someField: { _eq: $someFieldValue } }
|
|
) {
|
|
results {
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
someFieldValue,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someField).toEqual('string');
|
|
expect(getResult.data.objects.get.someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support Int numbers', async () => {
|
|
const someFieldValue = 123;
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('Number');
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!, $someFieldValue: Float) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(
|
|
where: { someField: { _eq: $someFieldValue } }
|
|
) {
|
|
results {
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
someFieldValue,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someField).toEqual('number');
|
|
expect(getResult.data.objects.get.someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support Float numbers', async () => {
|
|
const someFieldValue = 123.4;
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('Number');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!, $someFieldValue: Float) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(
|
|
where: { someField: { _eq: $someFieldValue } }
|
|
) {
|
|
results {
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
someFieldValue,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someField).toEqual('number');
|
|
expect(getResult.data.objects.get.someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support Boolean', async () => {
|
|
const someFieldValueTrue = true;
|
|
const someFieldValueFalse = false;
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someFieldTrue: someFieldValueTrue,
|
|
someFieldFalse: someFieldValueFalse,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someFieldTrue.type).toEqual('Boolean');
|
|
expect(schema.fields.someFieldFalse.type).toEqual('Boolean');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someFieldTrue: someFieldValueTrue,
|
|
someFieldFalse: someFieldValueFalse,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject(
|
|
$objectId: ID!
|
|
$someFieldValueTrue: Boolean
|
|
$someFieldValueFalse: Boolean
|
|
) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(
|
|
where: {
|
|
someFieldTrue: { _eq: $someFieldValueTrue }
|
|
someFieldFalse: { _eq: $someFieldValueFalse }
|
|
}
|
|
) {
|
|
results {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
someFieldValueTrue,
|
|
someFieldValueFalse,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someFieldTrue).toEqual(
|
|
'boolean'
|
|
);
|
|
expect(typeof getResult.data.objects.get.someFieldFalse).toEqual(
|
|
'boolean'
|
|
);
|
|
expect(getResult.data.objects.get.someFieldTrue).toEqual(true);
|
|
expect(getResult.data.objects.get.someFieldFalse).toEqual(false);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support Date', async () => {
|
|
const someFieldValue = {
|
|
__type: 'Date',
|
|
iso: new Date().toISOString(),
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('Date');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(where: { someField: { _exists: true } }) {
|
|
results {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someField).toEqual('object');
|
|
expect(getResult.data.objects.get.someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support createdAt', async () => {
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.createdAt.type).toEqual('Date');
|
|
|
|
const { createdAt } = createResult.data.objects.create;
|
|
expect(Date.parse(createdAt)).not.toEqual(NaN);
|
|
});
|
|
|
|
it('should support updatedAt', async () => {
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.updatedAt.type).toEqual('Date');
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.updatedAt).toEqual('string');
|
|
expect(Date.parse(getResult.data.objects.get.updatedAt)).not.toEqual(
|
|
NaN
|
|
);
|
|
});
|
|
|
|
it('should support pointer values', async () => {
|
|
const parent = new Parse.Object('ParentClass');
|
|
await parent.save();
|
|
|
|
const pointerFieldValue = {
|
|
__type: 'Pointer',
|
|
className: 'ParentClass',
|
|
objectId: parent.id,
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateChildObject($fields: Object) {
|
|
objects {
|
|
create(className: "ChildClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
pointerField: pointerFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('ChildClass').get();
|
|
expect(schema.fields.pointerField.type).toEqual('Pointer');
|
|
expect(schema.fields.pointerField.targetClass).toEqual('ParentClass');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateChildObject(
|
|
$fields1: ChildClassFields
|
|
$fields2: ChildClassFields
|
|
) {
|
|
objects {
|
|
createChildClass1: createChildClass(fields: $fields1) {
|
|
objectId
|
|
}
|
|
createChildClass2: createChildClass(fields: $fields2) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields1: {
|
|
pointerField: pointerFieldValue,
|
|
},
|
|
fields2: {
|
|
pointerField: pointerFieldValue.objectId,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetChildObject(
|
|
$objectId: ID!
|
|
$pointerFieldValue1: ParentClassPointer
|
|
$pointerFieldValue2: ParentClassPointer
|
|
) {
|
|
objects {
|
|
get(className: "ChildClass", objectId: $objectId)
|
|
findChildClass1: findChildClass(
|
|
where: { pointerField: { _eq: $pointerFieldValue1 } }
|
|
) {
|
|
results {
|
|
pointerField {
|
|
objectId
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
findChildClass2: findChildClass(
|
|
where: { pointerField: { _eq: $pointerFieldValue2 } }
|
|
) {
|
|
results {
|
|
pointerField {
|
|
objectId
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
pointerFieldValue1: pointerFieldValue,
|
|
pointerFieldValue2: pointerFieldValue.objectId,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.pointerField).toEqual(
|
|
'object'
|
|
);
|
|
expect(getResult.data.objects.get.pointerField).toEqual(
|
|
pointerFieldValue
|
|
);
|
|
expect(getResult.data.objects.findChildClass1.results.length).toEqual(
|
|
3
|
|
);
|
|
expect(getResult.data.objects.findChildClass2.results.length).toEqual(
|
|
3
|
|
);
|
|
});
|
|
|
|
it_only_db('mongo')('should support relation', async () => {
|
|
const someObject1 = new Parse.Object('SomeClass');
|
|
await someObject1.save();
|
|
const someObject2 = new Parse.Object('SomeClass');
|
|
await someObject2.save();
|
|
|
|
const pointerValue1 = {
|
|
__type: 'Pointer',
|
|
className: 'SomeClass',
|
|
objectId: someObject1.id,
|
|
};
|
|
const pointerValue2 = {
|
|
__type: 'Pointer',
|
|
className: 'SomeClass',
|
|
objectId: someObject2.id,
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateMainObject($fields: Object) {
|
|
objects {
|
|
create(className: "MainClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
relationField: {
|
|
__op: 'Batch',
|
|
ops: [
|
|
{
|
|
__op: 'AddRelation',
|
|
objects: [pointerValue1],
|
|
},
|
|
{
|
|
__op: 'AddRelation',
|
|
objects: [pointerValue2],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('MainClass').get();
|
|
expect(schema.fields.relationField.type).toEqual('Relation');
|
|
expect(schema.fields.relationField.targetClass).toEqual('SomeClass');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateMainObject($fields: MainClassFields) {
|
|
objects {
|
|
createMainClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
relationField: {
|
|
_op: 'Batch',
|
|
ops: [
|
|
{
|
|
_op: 'AddRelation',
|
|
objects: [pointerValue1],
|
|
},
|
|
{
|
|
_op: 'RemoveRelation',
|
|
objects: [pointerValue1],
|
|
},
|
|
{
|
|
_op: 'AddRelation',
|
|
objects: [pointerValue2],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetMainObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "MainClass", objectId: $objectId)
|
|
getMainClass(objectId: $objectId) {
|
|
relationField {
|
|
results {
|
|
objectId
|
|
createdAt
|
|
}
|
|
count
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.relationField).toEqual(
|
|
'object'
|
|
);
|
|
expect(getResult.data.objects.get.relationField).toEqual({
|
|
__type: 'Relation',
|
|
className: 'SomeClass',
|
|
});
|
|
expect(
|
|
getResult.data.objects.getMainClass.relationField.results.length
|
|
).toEqual(2);
|
|
expect(
|
|
getResult.data.objects.getMainClass.relationField.count
|
|
).toEqual(2);
|
|
|
|
const findResult = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObjects($where: Object) {
|
|
objects {
|
|
find(className: "SomeClass", where: $where) {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
$relatedTo: {
|
|
object: {
|
|
__type: 'Pointer',
|
|
className: 'MainClass',
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
key: 'relationField',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const compare = (obj1, obj2) =>
|
|
obj1.createdAt > obj2.createdAt ? 1 : -1;
|
|
|
|
expect(findResult.data.objects.find.results).toEqual(
|
|
jasmine.any(Array)
|
|
);
|
|
expect(findResult.data.objects.find.results.sort(compare)).toEqual(
|
|
[
|
|
{
|
|
objectId: someObject1.id,
|
|
createdAt: someObject1.createdAt.toISOString(),
|
|
updatedAt: someObject1.updatedAt.toISOString(),
|
|
},
|
|
{
|
|
objectId: someObject2.id,
|
|
createdAt: someObject2.createdAt.toISOString(),
|
|
updatedAt: someObject2.updatedAt.toISOString(),
|
|
},
|
|
].sort(compare)
|
|
);
|
|
});
|
|
|
|
it('should support files', async () => {
|
|
parseServer = await global.reconfigureServer({
|
|
publicServerURL: 'http://localhost:13377/parse',
|
|
});
|
|
|
|
const body = new FormData();
|
|
body.append(
|
|
'operations',
|
|
JSON.stringify({
|
|
query: `
|
|
mutation CreateFile($file: Upload!) {
|
|
files {
|
|
create(file: $file) {
|
|
name
|
|
url
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
file: null,
|
|
},
|
|
})
|
|
);
|
|
body.append('map', JSON.stringify({ 1: ['variables.file'] }));
|
|
body.append('1', 'My File Content', {
|
|
filename: 'myFileName.txt',
|
|
contentType: 'text/plain',
|
|
});
|
|
|
|
let res = await fetch('http://localhost:13377/graphql', {
|
|
method: 'POST',
|
|
headers,
|
|
body,
|
|
});
|
|
|
|
expect(res.status).toEqual(200);
|
|
|
|
const result = JSON.parse(await res.text());
|
|
|
|
expect(result.data.files.create.name).toEqual(
|
|
jasmine.stringMatching(/_myFileName.txt$/)
|
|
);
|
|
expect(result.data.files.create.url).toEqual(
|
|
jasmine.stringMatching(/_myFileName.txt$/)
|
|
);
|
|
|
|
const someFieldValue = {
|
|
__type: 'File',
|
|
name: result.data.files.create.name,
|
|
url: result.data.files.create.url,
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject(
|
|
$fields1: SomeClassFields
|
|
$fields2: SomeClassFields
|
|
) {
|
|
objects {
|
|
createSomeClass1: createSomeClass(fields: $fields1) {
|
|
objectId
|
|
}
|
|
createSomeClass2: createSomeClass(fields: $fields2) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields1: {
|
|
someField: someFieldValue,
|
|
},
|
|
fields2: {
|
|
someField: someFieldValue.name,
|
|
},
|
|
},
|
|
});
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('File');
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass1: findSomeClass(
|
|
where: { someField: { _exists: true } }
|
|
) {
|
|
results {
|
|
someField {
|
|
name
|
|
url
|
|
}
|
|
}
|
|
}
|
|
findSomeClass2: findSomeClass(
|
|
where: { someField: { _exists: true } }
|
|
) {
|
|
results {
|
|
someField {
|
|
name
|
|
url
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someField).toEqual('object');
|
|
expect(getResult.data.objects.get.someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass1.results.length).toEqual(
|
|
3
|
|
);
|
|
expect(getResult.data.objects.findSomeClass2.results.length).toEqual(
|
|
3
|
|
);
|
|
|
|
res = await fetch(getResult.data.objects.get.someField.url);
|
|
|
|
expect(res.status).toEqual(200);
|
|
expect(await res.text()).toEqual('My File Content');
|
|
});
|
|
|
|
it('should support object values', async () => {
|
|
const someFieldValue = { foo: 'bar' };
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('Object');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(where: { someField: { _exists: true } }) {
|
|
results {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
const { someField } = getResult.data.objects.get;
|
|
expect(typeof someField).toEqual('object');
|
|
expect(someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support array values', async () => {
|
|
const someFieldValue = [1, 'foo', ['bar'], { lorem: 'ipsum' }, true];
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('Array');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(where: { someField: { _exists: true } }) {
|
|
results {
|
|
objectId
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
const { someField } = getResult.data.objects.get;
|
|
expect(Array.isArray(someField)).toBeTruthy();
|
|
expect(someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support null values', async () => {
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someStringField: 'some string',
|
|
someNumberField: 123,
|
|
someBooleanField: true,
|
|
someObjectField: { someField: 'some value' },
|
|
someNullField: null,
|
|
},
|
|
},
|
|
});
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation UpdateSomeObject($objectId: ID!, $fields: Object) {
|
|
objects {
|
|
update(
|
|
className: "SomeClass"
|
|
objectId: $objectId
|
|
fields: $fields
|
|
) {
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
fields: {
|
|
someStringField: null,
|
|
someNumberField: null,
|
|
someBooleanField: null,
|
|
someObjectField: null,
|
|
someNullField: 'now it has a string',
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.someStringField).toBeFalsy();
|
|
expect(getResult.data.objects.get.someNumberField).toBeFalsy();
|
|
expect(getResult.data.objects.get.someBooleanField).toBeFalsy();
|
|
expect(getResult.data.objects.get.someObjectField).toBeFalsy();
|
|
expect(getResult.data.objects.get.someNullField).toEqual(
|
|
'now it has a string'
|
|
);
|
|
});
|
|
|
|
it('should support Bytes', async () => {
|
|
const someFieldValue = {
|
|
__type: 'Bytes',
|
|
base64: 'aGVsbG8gd29ybGQ=',
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('Bytes');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject(
|
|
$fields1: SomeClassFields
|
|
$fields2: SomeClassFields
|
|
) {
|
|
objects {
|
|
createSomeClass1: createSomeClass(fields: $fields1) {
|
|
objectId
|
|
}
|
|
createSomeClass2: createSomeClass(fields: $fields2) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields1: {
|
|
someField: someFieldValue,
|
|
},
|
|
fields2: {
|
|
someField: someFieldValue.base64,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!, $someFieldValue: Bytes) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(
|
|
where: { someField: { _eq: $someFieldValue } }
|
|
) {
|
|
results {
|
|
objectId
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
someFieldValue,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someField).toEqual('object');
|
|
expect(getResult.data.objects.get.someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
3
|
|
);
|
|
});
|
|
|
|
it('should support Geo Points', async () => {
|
|
const someFieldValue = {
|
|
__type: 'GeoPoint',
|
|
latitude: 45,
|
|
longitude: 45,
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('GeoPoint');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: {
|
|
latitude: someFieldValue.latitude,
|
|
longitude: someFieldValue.longitude,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(where: { someField: { _exists: true } }) {
|
|
results {
|
|
objectId
|
|
someField {
|
|
latitude
|
|
longitude
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.someField).toEqual('object');
|
|
expect(getResult.data.objects.get.someField).toEqual(someFieldValue);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support Polygons', async () => {
|
|
const someFieldValue = {
|
|
__type: 'Polygon',
|
|
coordinates: [[44, 45], [46, 47], [48, 49], [44, 45]],
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
somePolygonField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.somePolygonField.type).toEqual('Polygon');
|
|
|
|
await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
somePolygonField: someFieldValue.coordinates.map(point => ({
|
|
latitude: point[0],
|
|
longitude: point[1],
|
|
})),
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "SomeClass", objectId: $objectId)
|
|
findSomeClass(
|
|
where: { somePolygonField: { _exists: true } }
|
|
) {
|
|
results {
|
|
objectId
|
|
somePolygonField {
|
|
latitude
|
|
longitude
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
expect(typeof getResult.data.objects.get.somePolygonField).toEqual(
|
|
'object'
|
|
);
|
|
expect(getResult.data.objects.get.somePolygonField).toEqual(
|
|
someFieldValue
|
|
);
|
|
expect(getResult.data.objects.findSomeClass.results.length).toEqual(
|
|
2
|
|
);
|
|
});
|
|
|
|
it('should support polygon values', async () => {
|
|
const someFieldValue = {
|
|
__type: 'Polygon',
|
|
coordinates: [[1.0, 2.1], [3.2, 4.3], [5.4, 6.5], [1.0, 2.1]],
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: Object) {
|
|
objects {
|
|
create(className: "SomeClass", fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
somePolygonField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
getSomeClass(objectId: $objectId) {
|
|
somePolygonField {
|
|
latitude
|
|
longitude
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.create.objectId,
|
|
},
|
|
});
|
|
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.somePolygonField.type).toEqual('Polygon');
|
|
|
|
const { somePolygonField } = getResult.data.objects.getSomeClass;
|
|
expect(Array.isArray(somePolygonField)).toBeTruthy();
|
|
somePolygonField.forEach((coord, i) => {
|
|
expect(coord.latitude).toEqual(someFieldValue.coordinates[i][0]);
|
|
expect(coord.longitude).toEqual(someFieldValue.coordinates[i][1]);
|
|
});
|
|
});
|
|
|
|
it_only_db('mongo')('should support bytes values', async () => {
|
|
const SomeClass = Parse.Object.extend('SomeClass');
|
|
const someClass = new SomeClass();
|
|
someClass.set('someField', {
|
|
__type: 'Bytes',
|
|
base64: 'foo',
|
|
});
|
|
await someClass.save();
|
|
|
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
|
const schema = await new Parse.Schema('SomeClass').get();
|
|
expect(schema.fields.someField.type).toEqual('Bytes');
|
|
|
|
const someFieldValue = {
|
|
__type: 'Bytes',
|
|
base64: 'bytesContent',
|
|
};
|
|
|
|
const createResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation CreateSomeObject($fields: SomeClassFields) {
|
|
objects {
|
|
createSomeClass(fields: $fields) {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
fields: {
|
|
someField: someFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
getSomeClass(objectId: $objectId) {
|
|
someField
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.createSomeClass.objectId,
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.getSomeClass.someField).toEqual(
|
|
someFieldValue.base64
|
|
);
|
|
|
|
const updatedSomeFieldValue = {
|
|
__type: 'Bytes',
|
|
base64: 'newBytesContent',
|
|
};
|
|
|
|
const updatedResult = await apolloClient.mutate({
|
|
mutation: gql`
|
|
mutation UpdateSomeObject(
|
|
$objectId: ID!
|
|
$fields: SomeClassFields
|
|
) {
|
|
objects {
|
|
updateSomeClass(objectId: $objectId, fields: $fields) {
|
|
updatedAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: createResult.data.objects.createSomeClass.objectId,
|
|
fields: {
|
|
someField: updatedSomeFieldValue,
|
|
},
|
|
},
|
|
});
|
|
|
|
const { updatedAt } = updatedResult.data.objects.updateSomeClass;
|
|
expect(updatedAt).toBeDefined();
|
|
|
|
const findResult = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject($where: SomeClassConstraints!) {
|
|
objects {
|
|
findSomeClass(where: $where) {
|
|
results {
|
|
objectId
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
where: {
|
|
someField: {
|
|
_eq: updatedSomeFieldValue.base64,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
const findResults = findResult.data.objects.findSomeClass.results;
|
|
expect(findResults.length).toBe(1);
|
|
expect(findResults[0].objectId).toBe(
|
|
createResult.data.objects.createSomeClass.objectId
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Special Classes', () => {
|
|
it('should support User class', async () => {
|
|
const user = new Parse.User();
|
|
user.setUsername('user1');
|
|
user.setPassword('user1');
|
|
await user.signUp();
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_User", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: user.id,
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(user.id);
|
|
});
|
|
|
|
it('should support Installation class', async () => {
|
|
const installation = new Parse.Installation();
|
|
await installation.save({
|
|
deviceType: 'foo',
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_Installation", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: installation.id,
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(installation.id);
|
|
});
|
|
|
|
it('should support Role class', async () => {
|
|
const roleACL = new Parse.ACL();
|
|
roleACL.setPublicReadAccess(true);
|
|
const role = new Parse.Role('MyRole', roleACL);
|
|
await role.save();
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_Role", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: role.id,
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(role.id);
|
|
});
|
|
|
|
it('should support Session class', async () => {
|
|
const user = new Parse.User();
|
|
user.setUsername('user1');
|
|
user.setPassword('user1');
|
|
await user.signUp();
|
|
|
|
const session = await Parse.Session.current();
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_Session", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: session.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Session-Token': session.getSessionToken(),
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(session.id);
|
|
});
|
|
|
|
it('should support Product class', async () => {
|
|
const Product = Parse.Object.extend('_Product');
|
|
const product = new Product();
|
|
await product.save(
|
|
{
|
|
productIdentifier: 'foo',
|
|
icon: new Parse.File('icon', ['foo']),
|
|
order: 1,
|
|
title: 'Foo',
|
|
subtitle: 'My product',
|
|
},
|
|
{ useMasterKey: true }
|
|
);
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_Product", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: product.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(product.id);
|
|
});
|
|
|
|
it('should support PushStatus class', async () => {
|
|
const PushStatus = Parse.Object.extend('_PushStatus');
|
|
const pushStatus = new PushStatus();
|
|
await pushStatus.save(undefined, { useMasterKey: true });
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_PushStatus", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: pushStatus.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(pushStatus.id);
|
|
});
|
|
|
|
it('should support JobStatus class', async () => {
|
|
const JobStatus = Parse.Object.extend('_JobStatus');
|
|
const jobStatus = new JobStatus();
|
|
await jobStatus.save(undefined, { useMasterKey: true });
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_JobStatus", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: jobStatus.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(jobStatus.id);
|
|
});
|
|
|
|
it('should support JobSchedule class', async () => {
|
|
const JobSchedule = Parse.Object.extend('_JobSchedule');
|
|
const jobSchedule = new JobSchedule();
|
|
await jobSchedule.save(undefined, { useMasterKey: true });
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_JobSchedule", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: jobSchedule.id,
|
|
},
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(jobSchedule.id);
|
|
});
|
|
|
|
it('should support Hooks class', async () => {
|
|
const functionName = 'fooHook';
|
|
await parseServer.config.hooksController.saveHook({
|
|
functionName,
|
|
url: 'http://foo.bar',
|
|
});
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query FindSomeObject {
|
|
objects {
|
|
find(className: "_Hooks") {
|
|
results
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
context: {
|
|
headers: {
|
|
'X-Parse-Master-Key': 'test',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { results } = getResult.data.objects.find;
|
|
expect(results.length).toEqual(1);
|
|
expect(results[0].functionName).toEqual(functionName);
|
|
});
|
|
|
|
it('should support Audience class', async () => {
|
|
const Audience = Parse.Object.extend('_Audience');
|
|
const audience = new Audience();
|
|
await audience.save();
|
|
|
|
const getResult = await apolloClient.query({
|
|
query: gql`
|
|
query GetSomeObject($objectId: ID!) {
|
|
objects {
|
|
get(className: "_Audience", objectId: $objectId)
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
objectId: audience.id,
|
|
},
|
|
});
|
|
|
|
expect(getResult.data.objects.get.objectId).toEqual(audience.id);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|