Skip to content
This repository has been archived by the owner on Dec 17, 2018. It is now read-only.

Commit

Permalink
feat(graphql): Add strength training mutations
Browse files Browse the repository at this point in the history
Added mutations to allow creation of new strength training activities. Moved URLs into constants
file.
  • Loading branch information
Simon Wears committed Mar 22, 2017
1 parent 610f288 commit 4024d15
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 12 deletions.
2 changes: 2 additions & 0 deletions res/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
"COMMENTS": "The comments for the activity",
"EXERCISES": "The exercises performed during this strength training activity",
"NOTES": "Any notes that the user has associated with the activity (max. 1024 characters; optional)",
"POST_TO_FACEBOOK": "True to post this activity to Facebook, false to prevent posting (optional; if not specified, the user's default preference is used)",
"POST_TO_TWITTER": "True to post this activity to Twitter, false to prevent posting (optional; if not specified, the user's default preference is used)",
"SOURCE": "The name of the application that last modified this activity",
"START_TIME": "The starting time for the activity (e.g., Sat, 1 Jan 2011 00:00:00)",
"TOTAL_CALORIES": "The total calories burned (omitted if not available)",
Expand Down
8 changes: 8 additions & 0 deletions src/constants/healthgraph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
BASE_URL: 'https://api.runkeeper.com',
ROOT: '/user',

CONTENT_TYPES: {
strength_training_activities: 'application/vnd.com.runkeeper.NewStrengthTrainingActivity+json'
}
};
7 changes: 4 additions & 3 deletions src/data-types/strength-training-activities.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const GraphQLObjectType = graphql.GraphQLObjectType;
const GraphQLString = graphql.GraphQLString;
const GraphQLList = graphql.GraphQLList;
const GraphQLInt = graphql.GraphQLInt;
const GraphQLFloat = graphql.GraphQLFloat;
const CommentType = require('./comment');
const resolveItems = require('../helpers/resolve-items');
const i18n = require('../helpers/i18n');
Expand All @@ -20,7 +21,7 @@ const StrengthTrainingSet = new GraphQLObjectType({
},
weight: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.SET.WEIGHT'),
type: GraphQLInt
type: GraphQLFloat
}
}
});
Expand Down Expand Up @@ -101,14 +102,14 @@ const StrengthTrainingItem = new GraphQLObjectType({
resolve (parent, args, context) {
return context.healthGraphLoader.load(parent.uri).then(d => d.total_calories);
},
type: GraphQLInt
type: GraphQLFloat
},
userID: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.ITEM.USERID'),
resolve (parent, args, context) {
return context.healthGraphLoader.load(parent.uri).then(d => d.userID);
},
type: GraphQLString
type: GraphQLInt
}
}
});
Expand Down
9 changes: 5 additions & 4 deletions src/data-types/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const graphql = require('graphql');
const GraphQLObjectType = graphql.GraphQLObjectType;
const GraphQLString = graphql.GraphQLString;
const i18n = require('../helpers/i18n');
const HEALTHGRAPH = require('../constants/healthgraph');
const ProfileType = require('./profile');
const StrengthTrainingActivitiesType = require('./strength-training-activities').StrengthTrainingActivities;
const FitnessActivitiesType = require('./fitness-activities').FitnessActivities;
Expand All @@ -13,7 +14,7 @@ module.exports = new GraphQLObjectType({
description: i18n.t('GRAPHQL.USER.FITNESS_ACTIVITIES'),
resolve (parent, args, context) {
return context.healthGraphLoader
.load('/user')
.load(HEALTHGRAPH.ROOT)
.then(data => context.healthGraphLoader.load(data.fitness_activities));
},
type: FitnessActivitiesType
Expand All @@ -22,7 +23,7 @@ module.exports = new GraphQLObjectType({
description: i18n.t('GRAPHQL.USER.PROFILE'),
resolve (parent, args, context) {
return context.healthGraphLoader
.load('/user')
.load(HEALTHGRAPH.ROOT)
.then(data => context.healthGraphLoader.load(data.profile));
},
type: ProfileType
Expand All @@ -31,7 +32,7 @@ module.exports = new GraphQLObjectType({
description: i18n.t('GRAPHQL.USER.STRENGTH_TRAINING_ACTIVITIES'),
resolve (parent, args, context) {
return context.healthGraphLoader
.load('/user')
.load(HEALTHGRAPH.ROOT)
.then(data => context.healthGraphLoader.load(data.strength_training_activities));
},
type: StrengthTrainingActivitiesType
Expand All @@ -40,7 +41,7 @@ module.exports = new GraphQLObjectType({
description: i18n.t('GRAPHQL.USER.USERID'),
resolve (parent, args, context) {
return context.healthGraphLoader
.load('/user')
.load(HEALTHGRAPH.ROOT)
.then(data => data.userID);
},
type: GraphQLString
Expand Down
7 changes: 4 additions & 3 deletions src/helpers/healthgraph-loader.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
const request = require('superagent-promise')(require('superagent'), Promise);
const DataLoader = require('dataloader');
const log = require('./logging');
const HEALTHGRAPH = require('../constants/healthgraph');

module.exports = function createLoader (req) {
module.exports = function createLoader (access_token) {

return new DataLoader(
urls => Promise.all(urls.map(uri => {

log.info('HEALTHGRAPH_REQUEST', {uri});

return request
.get(`https://api.runkeeper.com${uri}`)
.get(`${HEALTHGRAPH.BASE_URL}${uri}`)
.query({
access_token: req.cookies.auth
access_token
})
.end()
.then(data => data.body);
Expand Down
184 changes: 184 additions & 0 deletions src/mutation-types/strength-training-activities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
const HEALTHGRAPH = require('../constants/healthgraph');
const graphql = require('graphql');
const GraphQLString = graphql.GraphQLString;
const GraphQLObjectType = graphql.GraphQLObjectType;
const GraphQLInputObjectType = graphql.GraphQLInputObjectType;
const GraphQLNonNull = graphql.GraphQLNonNull;
const GraphQLInt = graphql.GraphQLInt;
const GraphQLBoolean = graphql.GraphQLBoolean;
const GraphQLList = graphql.GraphQLList;
const GraphQLFloat = graphql.GraphQLFloat;
const GraphQLEnumType = graphql.GraphQLEnumType;
const i18n = require('../helpers/i18n');
const request = require('superagent-promise')(require('superagent'), Promise);


const MuscleTypes = new GraphQLEnumType({
name: 'MuscleTypes',
values: {
ABS: {value: 'Abs'},
ARMS: {value: 'Arms'},
BACK: {value: 'Back'},
CHEST: {value: 'Chest'},
LEGS: {value: 'Legs'},
SHOULDERS: {value: 'Shoulders'}
}
});

const ExerciseTypes = new GraphQLEnumType({
name: 'ExerciseTypes',
values: {
BARBELL_CURL: {value: 'Barbell Curl'},
DUMBBELL_CURL: {value: 'Dumbbell Curl'},
BARBELL_TRICEP_PRESS: {value: 'Barbell Tricep Press'},
DUMBBELL_TRICEP_PRESS: {value: 'Dumbbell Tricep Press'},
OVERHEAD_PRESS: {value: 'Overhead Press'},
WRIST_CURL: {value: 'Wrist Curl'},
TRICEP_KICKBACK: {value: 'Tricep Kickback'},
BENCH_PRESS: {value: 'Bench Press'},
CABLE_CROSSOVER: {value: 'Cable Crossover'},
DUBBELL_FLY: {value: 'Dumbbell Fly'},
INCLINE_BENCH: {value: 'Incline Bench'},
DIPS: {value: 'Dips'},
PUSHUP: {value: 'Pushup'},
PULLUP: {value: 'Pullup'},
BACK_RAISE: {value: 'Back Raise'},
BENT_OVER_ROW: {value: 'Bent-Over Row'},
SEATED_ROW: {value: 'Seated Row'},
CHINUP: {value: 'Chinup'},
LAT_PULLDOWN: {value: 'Lat Pulldown'},
SEATED_REVERSE_FLY: {value: 'Seated Reverse Fly'},
MILITARY_PRESS: {value: 'Military Press'},
UPRIGHT_ROW: {value: 'Upright Row'},
FRONT_RAISE: {value: 'Front Raise'},
SIDE_LATERAL_RAISE: {value: 'Side Lateral Raise'},
SNATCH: {value: 'Snatch'},
PUSH_PRESS: {value: 'Push Press'},
SHRUG: {value: 'Shrug'},
CRUNCH_MACHINE: {value: 'Crunch Machine'},
CRUNCH: {value: 'Crunch'},
AB_TWIST: {value: 'Ab Twist'},
BICYCLE_KICK: {value: 'Bicycle Kick'},
HANGING_LEG_RAISE: {value: 'Hanging Leg Raise'},
HANGING_KNEE_RAISE: {value: 'Hanging Knee Raise'},
REVERSE_CRUNCH: {value: 'Reverse Crunch'},
V_UP: {value: 'V Up'},
SITUP: {value: 'Situp'},
SQUAT: {value: 'Squat'},
LUNGE: {value: 'Lunge'},
DEAD_LIFT: {value: 'Dead Lift'},
HAMSTRING_CURL: {value: 'Hamstring Curl'},
GOOD_MORNING: {value: 'Good Morning'},
CLEAN: {value: 'Clean'},
LEG_PRESS: {value: 'Leg Press'},
LEG_EXTENSION: {value: 'Leg Extension'},
OTHER: {value: 'Other'}
}
});


/*
* TODO:
* Add response type (reuse query data-type?)
*/
module.exports = {

args: {
exercises: {
type: new GraphQLNonNull(new GraphQLList(new GraphQLInputObjectType({
name: 'StrengthTrainingExerciseMutation',
fields: {
notes: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.EXERCISE.NOTES'),
type: GraphQLString
},
primary_muscle_group: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.EXERCISE.PRIMARY_MUSCLE_GROUP'),
type: new GraphQLNonNull(MuscleTypes)
},
primary_type: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.EXERCISE.PRIMARY_TYPE'),
type: new GraphQLNonNull(ExerciseTypes)
},
routine: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.EXERCISE.ROUTINE'),
type: GraphQLString
},
secondary_muscle_group: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.EXERCISE.SECONDARY_MUSCLE_GROUP'),
type: MuscleTypes
},
secondary_type: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.EXERCISE.SECONDARY_TYPE'),
type: GraphQLString
},
sets: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.EXERCISE.SETS'),
type: new GraphQLNonNull(new GraphQLList(new GraphQLInputObjectType({
name: 'StrengthTrainingSetMutation',
fields: {
notes: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.SET.NOTES'),
type: GraphQLString
},
repetitions: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.SET.REPETITIONS'),
type: new GraphQLNonNull(GraphQLInt)
},
weight: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.SET.WEIGHT'),
type: new GraphQLNonNull(GraphQLFloat)
}
}
})))
}
}
})))
},
notes: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.ITEM.NOTES'),
type: GraphQLString
},
post_to_facebook: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.ITEM.POST_TO_FACEBOOK'),
type: GraphQLBoolean
},
post_to_twitter: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.ITEM.POST_TO_TWITTER'),
type: GraphQLBoolean
},
start_time: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.ITEM.START_TIME'),
type: new GraphQLNonNull(GraphQLString)
},
total_calories: {
description: i18n.t('GRAPHQL.STRENGTH_TRAINING.ITEM.TOTAL_CALORIES'),
type: GraphQLFloat
}
},

resolve (rootVal, args, context) {
return context.healthGraphLoader
.load(HEALTHGRAPH.ROOT)
.then(data => request
.post(`${HEALTHGRAPH.BASE_URL}${data.strength_training_activities}`)
.type(HEALTHGRAPH.CONTENT_TYPES.strength_training_activities)
.query({ access_token: context.access_token })
.send(Object.assign(args, {
start_time: new Date(args.start_time).toGMTString()
}))
.end()
);
},

// response query
type: new GraphQLObjectType({
name: 'StrengthTrainingActivityMutation',
fields: {
start_time: {
type: GraphQLString
}
}
})

};
15 changes: 13 additions & 2 deletions src/routes/graphql.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,30 @@ const graphql = require('graphql');
const GraphQLSchema = graphql.GraphQLSchema;
const UserType = require('../data-types/user');
const createLoader = require('../helpers/healthgraph-loader');
const StrengthTrainingType = require('../mutation-types/strength-training-activities');


module.exports = function (app) {

app.use('/graphql', graphqlHTTP(req => {

const healthGraphLoader = createLoader(req);
const access_token = req.cookies.auth;
const healthGraphLoader = createLoader(access_token);

return {
context: {
access_token,
healthGraphLoader
},
graphiql: true,
schema: new GraphQLSchema({
query: UserType
query: UserType,
mutation: new graphql.GraphQLObjectType({
name: 'healthgraph',
fields: {
strength_training_activities: StrengthTrainingType
}
})
})
}
})
Expand Down

0 comments on commit 4024d15

Please sign in to comment.