Skip to content

Commit

Permalink
Merge pull request #79 from Apollinaire/moviesOpenCrud
Browse files Browse the repository at this point in the history
example-movies openCRUD API
  • Loading branch information
SachaG authored Sep 4, 2018
2 parents a652274 + 7918177 commit 36e1155
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 163 deletions.
4 changes: 2 additions & 2 deletions .meteor/packages
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# see https://docs.vulcanjs.org/packages

vulcan:core@1.11.2
vulcan:core

############ Language Packages ############

Expand All @@ -25,4 +25,4 @@ getting-started
# example-membership
# example-interfaces
# example-reactions
# example-forms
# example-forms
15 changes: 10 additions & 5 deletions packages/example-movies/lib/components/movies/MoviesEditForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@ A component to configure the "edit movie" form.
*/

import React from 'react';
import { Components, registerComponent, getFragment } from "meteor/vulcan:core";
import { Components, registerComponent, getFragment } from 'meteor/vulcan:core';

import Movies from '../../modules/movies/collection.js';

const MoviesEditForm = ({documentId, closeModal}) =>

<Components.SmartForm
const MoviesEditForm = ({ documentId, closeModal, refetch }) => (
<Components.SmartForm
collection={Movies}
documentId={documentId}
mutationFragment={getFragment('MoviesItemFragment')}
showRemove={true}
successCallback={document => {
refetch();
closeModal();
}}
removeSuccessCallback={document => {
refetch();
closeModal();
}}
/>
);

registerComponent('MoviesEditForm', MoviesEditForm);
registerComponent({ name: 'MoviesEditForm', component: MoviesEditForm });
27 changes: 14 additions & 13 deletions packages/example-movies/lib/components/movies/MoviesItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,25 @@ import { Components, registerComponent } from 'meteor/vulcan:core';

import Movies from '../../modules/movies/collection.js';

const MoviesItem = ({movie, currentUser}) =>
const MoviesItem = ({ movie, currentUser, refetch }) => (
<div style={{ paddingBottom: '15px', marginBottom: '15px', borderBottom: '1px solid #ccc' }}>
{/* document properties */}

<div style={{paddingBottom: "15px",marginBottom: "15px", borderBottom: "1px solid #ccc"}}>
<h4>
{movie.name} ({movie.year})
</h4>
<p>
{movie.review}{movie.user && movie.user.displayName}
</p>

{/* document properties */}

<h4>{movie.name} ({movie.year})</h4>
<p>{movie.review}{movie.user && movie.user.displayName}</p>

{/* edit document form */}

{Movies.options.mutations.edit.check(currentUser, movie) ?
{Movies.options.mutations.update.check(currentUser, movie) ? (
<Components.ModalTrigger label="Edit Movie" title="Edit Movie">
<Components.MoviesEditForm currentUser={currentUser} documentId={movie._id} />
<Components.MoviesEditForm currentUser={currentUser} documentId={movie._id} refetch={refetch} />
</Components.ModalTrigger>
: null
}

) : null}
</div>
);

registerComponent('MoviesItem', MoviesItem);
registerComponent({ name: 'MoviesItem', component: MoviesItem });
48 changes: 19 additions & 29 deletions packages/example-movies/lib/components/movies/MoviesList.jsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,52 @@
/*
List of movies.
Wrapped with the "withList" and "withCurrentUser" containers.
Wrapped with the "withMulti" and "withCurrentUser" containers.
*/

import React from 'react';
import { registerComponent, Components, withList, withCurrentUser, Loading } from 'meteor/vulcan:core';
import Helmet from 'react-helmet';
import { registerComponent, replaceComponent, Components, withMulti, withCurrentUser, Loading } from 'meteor/vulcan:core';

import Movies from '../../modules/movies/collection.js';

const MoviesList = ({results = [], currentUser, loading, loadMore, count, totalCount}) =>

<div style={{maxWidth: '500px', margin: '20px auto'}}>

<Helmet>
<link name="bootstrap" rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"/>
</Helmet>

const MoviesList = ({ results = [], currentUser, loading, loadMore, count, totalCount, refetch }) => (
<div style={{ maxWidth: '500px', margin: '20px auto' }}>
{/* user accounts */}

<div style={{padding: '20px 0', marginBottom: '20px', borderBottom: '1px solid #ccc'}}>

<div style={{ padding: '20px 0', marginBottom: '20px', borderBottom: '1px solid #ccc' }}>
<Components.AccountsLoginForm />

</div>

{loading ?

<Loading /> :

{loading ? (
<Loading />
) : (
<div className="movies">

{/* new document form */}

<Components.MoviesNewForm />
<Components.MoviesNewForm refetch={refetch} />

{/* documents list */}

{results.map(movie => <Components.MoviesItem key={movie._id} movie={movie} currentUser={currentUser} />)}

{results.map(movie => {
return <Components.MoviesItem key={movie._id} movie={movie} currentUser={currentUser} refetch={refetch} />;
})}

{/* load more */}

{totalCount > results.length ?
{ totalCount > results.length ?
<a href="#" onClick={e => {e.preventDefault(); loadMore();}}>Load More ({count}/{totalCount})</a> :
<p>No more items.</p>
}

}
</div>
}

)}
</div>
);

const options = {
collection: Movies,
fragmentName: 'MoviesItemFragment',
limit: 5
limit: 5,
};

registerComponent('MoviesList', MoviesList, withCurrentUser, [withList, options]);
registerComponent({ name: 'MoviesList', component: MoviesList, hocs: [withCurrentUser, [withMulti, options]] });
22 changes: 8 additions & 14 deletions packages/example-movies/lib/components/movies/MoviesNewForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,15 @@ import { Components, registerComponent, withCurrentUser, getFragment } from 'met

import Movies from '../../modules/movies/collection.js';

const MoviesNewForm = ({currentUser}) =>

const MoviesNewForm = ({ currentUser, refetch }) => (
<div>

{Movies.options.mutations.new.check(currentUser) ?
<div style={{marginBottom: '20px', paddingBottom: '20px', borderBottom: '1px solid #ccc'}}>
{Movies.options.mutations.create.check(currentUser) ? (
<div style={{ marginBottom: '20px', paddingBottom: '20px', borderBottom: '1px solid #ccc' }}>
<h4>Insert New Document</h4>
<Components.SmartForm
collection={Movies}
mutationFragment={getFragment('MoviesItemFragment')}
/>
</div> :
null
}

<Components.SmartForm collection={Movies} mutationFragment={getFragment('MoviesItemFragment')} successCallback={refetch} />
</div>
) : null}
</div>
);

registerComponent('MoviesNewForm', MoviesNewForm, withCurrentUser);
registerComponent({ name: 'MoviesNewForm', component: MoviesNewForm, hocs: [withCurrentUser] });
12 changes: 6 additions & 6 deletions packages/example-movies/lib/modules/movies/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The main Movies collection definition file.
*/

import { createCollection } from 'meteor/vulcan:core';
import { createCollection, getDefaultResolvers, getDefaultMutations, } from 'meteor/vulcan:core';
import schema from './schema.js';
import resolvers from './resolvers.js';
import './fragments.js';
Expand All @@ -13,17 +13,17 @@ import './permissions.js';
import './parameters.js';

const Movies = createCollection({

collectionName: 'Movies',

typeName: 'Movie',

schema,

resolvers,
schema: schema,

mutations,
resolvers: resolvers,
// resolvers: getDefaultResolvers({typeName:'Movie'}),

mutations: mutations,
// mutations: getDefaultMutations({typeName: 'Movie'}),
});

export default Movies;
95 changes: 48 additions & 47 deletions packages/example-movies/lib/modules/movies/mutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
Define the three default mutations:
- new (e.g.: moviesNew(document: moviesInput) : Movie )
- edit (e.g.: moviesEdit(documentId: String, set: moviesInput, unset: moviesUnset) : Movie )
- remove (e.g.: moviesRemove(documentId: String) : Movie )
- create (e.g.: createMovie(data: {document: CreateMovieDataInput!}) : MovieOutput )
- update (e.g.: updateMovie(selector: MovieSelectorUniqueInput!, data: UpdateMovieDataInput!) : MovieOutput )
- delete (e.g.: deleteMovie(selector: MovieSelectorUniqueInput!) : MovieOutput )
Each mutation has:
Expand All @@ -14,87 +14,88 @@ Each mutation has:
*/

import { newMutation, editMutation, removeMutation, Utils } from 'meteor/vulcan:core';
import {
createMutator,
updateMutator,
deleteMutator,
Utils,
} from 'meteor/vulcan:core';
import Users from 'meteor/vulcan:users';

const mutations = {
create: {
name: 'moviesCreate',

new: {

name: 'moviesNew',

check(user) {
if (!user) return false;
return Users.canDo(user, 'movies.new');
if (!user) return false; //the user must be logged in
return Users.canDo(user, 'movies.create'); // the user must have the permission to do the mutation. For this, see permissions.js
},

mutation(root, {document}, context) {

mutation(root, args, context) {
const { data: document } = args;

//run the check function defined above
Utils.performCheck(this.check, context.currentUser, document);

return newMutation({
collection: context.Movies,
document: document,
currentUser: context.currentUser,
validate: true,
return createMutator({
collection: context.Movies, // the collection we are creating a document in
document: document, // the document inserted in the mutation input
currentUser: context.currentUser, // the user doing the mutation
validate: true,
context,
});
},

},

edit: {

name: 'moviesEdit',

update: {
name: 'moviesUpdate',

check(user, document) {
if (!user || !document) return false;
return Users.owns(user, document) ? Users.canDo(user, 'movies.edit.own') : Users.canDo(user, `movies.edit.all`);
return Users.owns(user, document)
? Users.canDo(user, 'movies.update.own')
: Users.canDo(user, `movies.update.all`);
},

mutation(root, {documentId, set, unset}, context) {

const document = context.Movies.findOne(documentId);
mutation(root, {selector, data}, context) {
const document = context.Movies.findOne( {_id: selector.documentId || selector._id});
// Utils.performCheck is applying this.check(context.currentUser, document); and returns an error if the result is false or if there is no user or document, ending the mutation before updating the db
Utils.performCheck(this.check, context.currentUser, document);

return editMutation({
collection: context.Movies,
documentId: documentId,
set: set,
unset: unset,
currentUser: context.currentUser,
validate: true,
return updateMutator({
collection: context.Movies, // the collection we are updating
selector: selector, //the way to select the document to update
data: data, // the new value of the document
currentUser: context.currentUser, // the user performing the update
validate: true, //if we should validate or not
context,
});
},

},

remove: {

name: 'moviesRemove',

delete: {
name: 'moviesDelete',

check(user, document) {
if (!user || !document) return false;
return Users.owns(user, document) ? Users.canDo(user, 'movies.remove.own') : Users.canDo(user, `movies.remove.all`);
return Users.owns(user, document)
? Users.canDo(user, 'movies.delete.own')
: Users.canDo(user, `movies.delete.all`);
},

mutation(root, {documentId}, context) {

const document = context.Movies.findOne(documentId);
mutation(root, { selector }, context) {
const document = context.Movies.findOne({ _id: selector.documentId || selector._id });
Utils.performCheck(this.check, context.currentUser, document);

return removeMutation({
collection: context.Movies,
documentId: documentId,
return deleteMutator({
collection: context.Movies,
selector: selector,
currentUser: context.currentUser,
validate: true,
context,
});
},

},

};

export default mutations;
2 changes: 1 addition & 1 deletion packages/example-movies/lib/modules/movies/parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ function sortByYear (parameters, terms) {
};
}

addCallback('movies.parameters', sortByYear);
addCallback('movie.parameters', sortByYear);
Loading

0 comments on commit 36e1155

Please sign in to comment.