Skip to content

Commit

Permalink
Merge pull request #143 from matortheeternal/dev
Browse files Browse the repository at this point in the history
Public Beta Release v1.2
  • Loading branch information
matortheeternal authored Jan 12, 2017
2 parents 31434d6 + 8fc41f4 commit ec148fe
Show file tree
Hide file tree
Showing 101 changed files with 32,398 additions and 11 deletions.
29 changes: 29 additions & 0 deletions mod-picker/app/assets/javascripts/BackendAPI/apiTokenService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
app.service('apiTokenService', function(backend) {
var service = this;

this.retrieveUserTokens = function(userId) {
return backend.retrieve('/users/' + userId + '/api_tokens');
};

this.expireToken = function(tokenId) {
return backend.delete('/api_tokens/' + tokenId);
};

this.getTokenPostData = function(name) {
return {
api_token: {
name: name
}
};
};

this.createToken = function(name) {
var postData = service.getTokenPostData(name);
return backend.post('/api_tokens', postData);
};

this.updateToken = function(token) {
var postData = service.getTokenPostData(token.name);
return backend.update('/api_tokens/' + token.id, postData);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
app.directive('editTokenModal', function() {
return {
restrict: 'E',
templateUrl: '/resources/directives/shared/editTokenModal.html',
controller: 'editTokenModalController',
scope: false
}
});

app.controller('editTokenModalController', function($scope, apiTokenService, formUtils, eventHandlerFactory) {
// inherited functions
$scope.unfocusTokenModal = formUtils.unfocusModal($scope.toggleTokenModal);

// shared function setup
eventHandlerFactory.buildModalMessageHandlers($scope);

$scope.saveToken = function() {
apiTokenService.updateToken($scope.activeToken).then(function() {
$scope.$emit('modalSuccessMessage', 'Updated API Token "' + $scope.activeToken.name + '"successfully');
$scope.$applyAsync(function() {
$scope.originalToken.name = $scope.activeToken.name;
});
}, function(response) {
var params = {
text: 'Error updating API token: '+$scope.activeToken.name,
response: response
};
$scope.$emit('modalErrorMessage', params);
});
};
});
23 changes: 23 additions & 0 deletions mod-picker/app/assets/javascripts/Factories/actionsFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,27 @@ app.service('actionsFactory', function() {
}
}]
};

/* api token actions */
this.apiTokenActions = function() {
return [{
caption: "Edit",
title: "Edit this API Token's name",
hidden: function($scope, item) {
return item.expired;
},
execute: function($scope, item) {
$scope.$emit('editToken', item);
}
}, {
caption: "Expire",
title: "Expire this API token so it is no longer valid",
disabled: function($scope, item) {
return item.expired;
},
execute: function($scope, item) {
$scope.$emit('expireToken', item);
}
}]
};
});
47 changes: 47 additions & 0 deletions mod-picker/app/assets/javascripts/Factories/columnsFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -1163,4 +1163,51 @@ app.service('columnsFactory', function() {
this.tagColumnGroups = function() {
return ["General"];
};

this.apiTokenColumns = function() {
return [
{
group: "General",
visibility: true,
required: true,
label: "Name",
data: "name",
invertSort: true,
dynamic: true
},
{
group: "General",
visibility: true,
label: "API Key",
data: "api_key",
invertSort: true
},
{
group: "General",
visibility: true,
label: "Requests",
data: "requests_count",
filter: "number"
},
{
group: "General",
visibility: true,
label: "Created",
data: "created",
filter: "date"
},
{
group: "General",
visibility: true,
label: "Expired",
data: "date_expired",
filter: "date",
dynamic: true
}
]
};

this.apiTokenColumnGroups = function() {
return ["General"];
};
});
1 change: 0 additions & 1 deletion mod-picker/app/assets/javascripts/Factories/sortFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ app.service("sortFactory", function() {
factory.buildCountSortOption("Stars"),
factory.buildSortOption("Reputation"),
factory.buildSortOption("Avg Rating", "average_rating"),
factory.buildSortOption("Plugins", "plugins_count"),
factory.buildCountSortOption("Plugins"),
factory.buildCountSortOption("Mod Lists"),
factory.buildCountSortOption("Required Mods"),
Expand Down
3 changes: 2 additions & 1 deletion mod-picker/app/assets/javascripts/Factories/tabsFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ app.service("tabsFactory", function() {
{ name: 'Profile' },
{ name: 'Account' },
{ name: 'Mod Lists' },
{ name: 'Authored Mods' }
{ name: 'Authored Mods' },
{ name: 'API Access' }
];
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ app.config(['$stateProvider', function($stateProvider) {
}
},
url: '/mods'
}).state('base.settings.API Access', {
sticky: true,
deepStateRedirect: true,
views: {
'API Access': {
templateUrl: '/resources/partials/userSettings/apiAccess.html',
controller: 'userSettingsApiController'
}
},
url: '/api-access'
});
}]);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
app.controller('userSettingsApiController', function($scope, $rootScope, $timeout, columnsFactory, actionsFactory, apiTokenService) {
// initialize variables
$scope.currentUser = $rootScope.currentUser;
$scope.actions = actionsFactory.apiTokenActions();
$scope.columns = columnsFactory.apiTokenColumns();
$scope.columnGroups = columnsFactory.apiTokenColumnGroups();

// BASE RETRIEVAL LOGIC
$scope.retrieveApiTokens = function() {
var userId = $scope.currentUser.id;
apiTokenService.retrieveUserTokens(userId).then(function(data) {
$scope.api_tokens = data.api_tokens;
}, function(response) {
$scope.errors.api_tokens = response;
});
};

//retrieve the mod lists when the state is first loaded
$scope.retrieveApiTokens();

// toggles the token modal
$scope.toggleTokenModal = function(visible) {
$scope.$emit('toggleModal', visible);
$scope.showTokenModal = visible;
};

// create a new api token
$scope.newApiToken = function() {
apiTokenService.createToken("API Token").then(function(data) {
$scope.api_tokens.push(data.api_token);
}, function(response) {
var params = {
label: 'Error creating API Token',
response: response
};
$scope.$emit('errorMessage', params);
})
};

// action handlers
$scope.$on('editToken', function(event, token) {
$scope.activeToken = angular.copy(token);
$scope.originalToken = token;
$scope.toggleTokenModal(true);
});

$scope.$on('expireToken', function(event, token) {
apiTokenService.expireToken(token.id).then(function() {
$timeout(function() {
$scope.$apply(function() {
token.expired = true;
token.date_expired = (new Date()).toISOString();
});
});
$scope.$emit('successMessage', 'API Token expired successfully.');
}, function(response) {
var params = {
label: 'Error expiring API Token',
response: response
};
$scope.$emit('errorMessage', params);
});
});
});
46 changes: 46 additions & 0 deletions mod-picker/app/controllers/api/api_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
class Api::ApiController < ActionController::Base
before_filter :verify_api_token

# Render errors as appropriate
def render_standard_error(exception, status)
error_hash = { error: exception.message }
error_hash[:backtrace] = exception.backtrace unless Rails.env.production?
render json: error_hash, status: status
end

rescue_from ::StandardError do |exception|
render_standard_error(exception, 500)
end

rescue_from ActiveRecord::RecordNotFound do |exception|
render_standard_error(exception, 404)
end

rescue_from Exceptions::ModExistsError do |exception|
render json: exception.response_object, status: 500
end

def verify_api_token
@api_token = ApiToken.find_by(expired: false, api_key: params[:api_key])
if @api_token.present?
@api_token.increment_requests!
else
render json: { error: "Your API key is invalid." }, status: 401
end
end

def current_ability
@current_ability ||= Ability.new(nil)
end

def json_format(resource, format=nil)
format ||= action_name.to_sym
resource.as_json(format: format)
end

def respond_with_json(resource, format=nil, root=nil)
format ||= action_name.to_sym
resource_json = resource.as_json(format: format)
render json: root ? { root => resource_json } : resource_json
end
end
6 changes: 6 additions & 0 deletions mod-picker/app/controllers/api/v1/categories_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Api::V1::CategoriesController < Api::ApiController
# GET /categories
def index
render json: Category.all
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Api::V1::CategoryPrioritiesController < Api::ApiController
# GET /category_priorities.json
def index
render json: CategoryPriority.all
end
end
32 changes: 32 additions & 0 deletions mod-picker/app/controllers/api/v1/comments_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class Api::V1::CommentsController < Api::ApiController
before_action :set_comment, only: [:show]

# GET /comments
def index
@comments = Comment.eager_load(:submitter => :reputation).preload(:commentable).accessible_by(current_ability).filter(filtering_params).sort(params[:sort]).paginate(page: params[:page])
count = Comment.eager_load(:submitter => :reputation).accessible_by(current_ability).filter(filtering_params).count

render json: {
comments: json_format(@comments),
max_entries: count,
entries_per_page: Comment.per_page
}
end

# GET /comments/1
def show
authorize! :read, @comment
respond_with_json(@comment)
end

private
# Use callbacks to share common setup or constraints between actions.
def set_comment
@comment = Comment.find(params[:id])
end

# Params we allow filtering on
def filtering_params
params[:filters].slice(:adult, :hidden, :search, :submitter, :include_replies, :commentable, :replies, :submitted, :edited)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Api::V1::CompatibilityNotesController < Api::V1::ContributionsController
before_action :set_compatibility_note, only: [:show, :corrections, :history]

# GET /compatibility_notes
def index
# prepare compatibility notes
@compatibility_notes = CompatibilityNote.preload(:editor, :editors, :first_mod, :second_mod, :compatibility_mod, :compatibility_plugin, :compatibility_mod_option).eager_load(submitter: :reputation).accessible_by(current_ability).filter(filtering_params).sort(params[:sort]).paginate(page: params[:page])
count = CompatibilityNote.eager_load(submitter: :reputation).accessible_by(current_ability).filter(filtering_params).count

# render response
render json: {
compatibility_notes: @compatibility_notes,
max_entries: count,
entries_per_page: CompatibilityNote.per_page
}
end

private
# Use callbacks to share common setup or constraints between actions.
def set_compatibility_note
@contribution = CompatibilityNote.find(params[:id])
end

# Params we allow filtering on
def filtering_params
params[:filters].slice(:adult, :hidden, :approved, :game, :search, :status, :submitter, :editor, :helpfulness, :reputation, :helpful_count, :not_helpful_count, :standing, :corrections_count, :history_entries_count, :submitted, :edited);
end
end
20 changes: 20 additions & 0 deletions mod-picker/app/controllers/api/v1/contributions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Api::V1::ContributionsController < Api::ApiController
# GET /contribution/1
def show
authorize! :read, @contribution
render json: @contribution
end

# POST/GET /contribution/1/corrections
def corrections
authorize! :read, @contribution

# prepare corrections
corrections = @contribution.corrections.accessible_by(current_ability)

# render response
render json: {
corrections: corrections
}
end
end
Loading

0 comments on commit ec148fe

Please sign in to comment.