Skip to content

Commit

Permalink
Add an UI to configure providers #9 (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
javiertuya authored Feb 22, 2024
1 parent 6403526 commit d44b4cc
Show file tree
Hide file tree
Showing 6 changed files with 441 additions and 121 deletions.
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ A provider is defined by a repository type (GitHub, GitLab), an *user* and an ac
You can define any combination (e.g. providers with the same username but different token, or different username but same token).

### Api acces token encryption
As told before, the configuration is stored in the browser local memory as all processing occours in the browser.
As mentioned before, the configuration is stored in the browser local memory and all processing occours in the browser.
To protect sensistive information (the access tokens) the user is given the option to encrypt the tokens using a password.
If you set a password, the next time that you open a tab with DasGit you will be asked for the password.
If you set a password, the next time that you open DashGit you will be asked for the password.

If you forgot the password, you are given with the option of skip. In that case the api calls will fail and you should go
If you forgot the password, you are given with the option of skip. In that case you may notice that api calls fail, you should go
to the configuration to reset the tokens to an empty value.
Note that once a token is encrypted, you can't decrypt it, only reset.

Expand All @@ -45,36 +45,39 @@ The user can customize how the work items are sorted and organized by setting th
This settings are no stored in the configuration.

### Scope configuration
The user is the reference username for which the work items are displayed (assigned to, created by, etc.)
The username is the reference user for which the work items are displayed (assigned to, created by, etc.)
and the token defines the scope of the request that determines what items are displayed.
Note that the username can be someone other the token owner.

- The scope of Assigned, Involved and Created views is whatever repository visible for the token.
- The scope of Unassigned and Dependabot views is restricted only to the repository of the token owner.
If you need include other users or organizations, you must include them in the `unassignedAdditionalOwner`
or `dependabotAdditionalOwner` parameters, respectively.
- The scope of Unassigned and Dependabot views is restricted to the repository of the token owner.
If you need include other users or organizations, you must include them in the `Add owners to unassigned`
or `Add owners to dependabot` parameters, respectively.
- The scope of Branches view is handled differently, as data is obtained by the GraphQL API requests insetead of the REST API.
On GitHub you should specify one or more than the following scopes: OWNER, ORGANIZATION_MEMBER or COLLABORATOR.
On GitHub you will specify one or more than the following scopes: OWNER, ORGANIZATION_MEMBER or COLLABORATOR.

### Filtering
The requests made against the repositories get the most recent work items that fit on a single response page,
that is enough for the most common use case to display the open work items regarding the user.
Moreover the data displayed can be restricted by setting any of the following parameters:
- `maxAge`: Filters out the work items that are older than the days specified.
- `filterIfLabel`: Filters out the work items that contain the label specified.
- `Max age`: Filters out the work items that are older than the days specified.
- `Filter if Label`: Filters out the work items that contain the label specified.

### Status cache
Requests to the GraphQL API to get the branches and build statuses are expensive if they retrieve data
from many repositories and are subject to more restrictive rate limits than REST API.
To mitigate potential problems and improve the UI response times, these calls are cached and managed
To mitigate potential problems with API rate limits and improve the UI response times, these calls are cached and managed
by two parameters (measured in seconds):
- `statusCacheUpdateTime`: During this period, any call to get statuses returns the cached data-
This is to avoid makking API calls when the user moves from a view to another in a short period of time.
- `Status Cache Update Time`: During this period, any call to get statuses returns the cached data.
This is to avoid making API calls when the user moves from a view to another in a short period of time.
When this period of time expires, the cache will be incrementally updated by requesting
data only from the latest updated projects.
- `statusCacheRefreshTime`: It specifies a much longer period than `statusCacheUpdateTime`.
- `Status Cache Refresh Time`: It specifies a much longer period than `Status Cache Update Time`.
When this period of time expires, the cache is fully refreshed.

## Contributing

This repository follows the general contribution policies and guidelines at the giis-uniovi org:
[CONTRIBUTING.md](https://github.com/giis-uniovi/.github/blob/main/profile/CONTRIBUTING.md)

If you plan to make any contribution, please, first create an issue to discuss the approach before starting development.
56 changes: 29 additions & 27 deletions dashgit-web/app/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,35 +58,37 @@ const config = {
this.setDefault(data, "statusCacheRefreshTime", 3600);
this.setDefault(data, "maxAge", 0);
this.setDefault(data, "providers", []);
for (const element of data.providers) {
let provider = element;
this.setDefault(element, "provider", "");
this.setDefault(element, "uid", "");
this.setDefault(element, "user", "");
this.setDefault(element, "token", "");
this.setDefault(element, "enabled", true);
this.setDefault(element, "enableNotifications", true);
this.setDefault(element, "filterIfLabel", "");
if (provider.provider == "GitHub") {
this.setDefault(element, "url", "https://github.com");
this.setDefault(element, "api", "https://api.github.com");
this.setDefault(element, "unassignedAdditionalOwner", []);
this.setDefault(element, "dependabotAdditionalOwner", []);
this.setDefault(element, "graphql", {});
this.setDefault(element.graphql, "ownerAffiliations", ["OWNER"]);
this.setDefault(element.graphql, "maxProjects", 20);
this.setDefault(element.graphql, "maxBranches", 10);
} else if (provider.provider == "GitLab") {
this.setDefault(element, "url", "");
this.setDefault(element, "dependabotUser", "dependabot");
this.setDefault(element, "graphql", {});
this.setDefault(element.graphql, "maxProjects", 20);
this.setDefault(element.graphql, "maxBranches", 10);
this.setDefault(element.graphql, "maxPipelines", 100);
}
}
for (const provider of data.providers)
this.setProviderDefaults(provider);
return data;
},
setProviderDefaults: function(element) {
this.setDefault(element, "provider", "");
this.setDefault(element, "uid", "");
this.setDefault(element, "user", "");
this.setDefault(element, "token", "");
this.setDefault(element, "enabled", true);
this.setDefault(element, "enableNotifications", true);
this.setDefault(element, "filterIfLabel", "");
if (element.provider == "GitHub") {
this.setDefault(element, "url", "https://github.com");
this.setDefault(element, "api", "https://api.github.com");
this.setDefault(element, "unassignedAdditionalOwner", []);
this.setDefault(element, "dependabotAdditionalOwner", []);
this.setDefault(element, "graphql", {});
this.setDefault(element.graphql, "ownerAffiliations", ["OWNER"]);
this.setDefault(element.graphql, "maxProjects", 20);
this.setDefault(element.graphql, "maxBranches", 10);
} else if (element.provider == "GitLab") {
this.setDefault(element, "url", "");
this.setDefault(element, "dependabotUser", "dependabot");
this.setDefault(element, "graphql", {});
this.setDefault(element.graphql, "maxProjects", 20);
this.setDefault(element.graphql, "maxBranches", 10);
this.setDefault(element.graphql, "maxPipelines", 100);
}
return element;
},
setDefault: function (parent, property, value) {
if (parent[property] == undefined || parent[property] == null)
parent[property] = value;
Expand Down
98 changes: 75 additions & 23 deletions dashgit-web/app/ConfigController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,49 @@ import { configView } from "./ConfigView.js"
/**
* Manages the dialog at the config tab
*/

//Switch between nav links
$(document).on('click', '#config-providers', function (e) {
configView.renderConfigData($(this), config.data);
});
$(document).on('click', '#config-import-export', function (e) {
configView.renderImportExport($(this), config.data);
});
$(document).on('click', '#config-encrypt', function (e) {
configView.renderEncrypt($(this), config.data.encrypted);
});
$(document).on('click', '#config-reset', function (e) {
configView.renderDecrypt($(this), config.data.encrypted);
});

// actions on events
$(document).on('click', '.config-btn-add-github', function (e) {
configView.addProvider(config.setProviderDefaults({ provider: "GitHub" }));
});
$(document).on('click', '.config-btn-add-gitlab', function (e) {
configView.addProvider(config.setProviderDefaults({ provider: "GitLab" }));
});
$(document).on('click', '.config-btn-provider-remove', function (e) {
configView.removeProvider($(this));
});
$(document).on('click', '.config-btn-provider-down', function (e) {
configView.moveProvider($(this), +1);
});
$(document).on('click', '.config-btn-provider-up', function (e) {
configView.moveProvider($(this), -1);
});

// Data update events

$(document).on('click', '.config-btn-provider-submit', function (e) {
if ($(".config-form")[0].checkValidity()) {
console.log("Saving config data");
configController.saveData();
e.preventDefault();
} else
console.log("Can't save config data due to validation issues");
});

$(document).on('click', '#inputEncryptButton', function (e) {
if ($('#inputEncryptPassword').val().length > 0) {
config.xtoken = $("#inputEncryptPassword").val();
Expand All @@ -14,7 +57,7 @@ $(document).on('click', '#inputEncryptButton', function (e) {
config.save();
$(this).closest("form")[0].reset();
e.preventDefault()
configController.updateTab();
configController.updateMainTarget();
} else {
$('#inputEncryptPassword')[0].setCustomValidity("Please, enter a password to encrypt the token");
$('#inputEncryptPassword')[0].reportValidity();
Expand All @@ -28,45 +71,54 @@ $(document).on('click', '#inputDecryptButton', function (e) {
provider.token = "";
config.data.encrypted = false;
config.save();
configController.updateTab();
configController.updateMainTarget();
e.preventDefault();
});

$(document).on('click', '#buttonConfigSave', function () {
console.log("Save config");
$(document).on('click', '#buttonConfigSave', function (e) {
console.log("Save config json");
try {
config.updateFromString($("#configJson").val());
configController.updateTab();
configController.updateMainTarget();
//to have a refresh effect when changing later to another tab
wiController.reset(true);
wiView.reset();
configController.displayToast("Configuration saved");
} catch (error) {
wiView.renderAlert("danger", error);
}
e.preventDefault();
});

const configController = {
updateTab: function () {
configView.render();
if (config.data.encrypted)
this.encryptedMode();
else
this.decryptedMode();
//setup config view, goes to provider configuration by default
updateMainTarget: function () {
configView.renderHeader();
configView.renderConfigData($("#config-providers"), config.data);
},

saveData: function () {
// Creates a local config data object to get common data from the ui
let data = config.setAllDefaults({});
data = configView.html2common(data);

// Finds each provider data in the ui and adds the provider to this config data object
for (let item of configView.getProviders()) {
let provider = config.setProviderDefaults({ provider: item.type });
provider = configView.html2provider(provider, item.key);
data.providers.push(provider);
}

//Display message if any GitHub provider does not specify token.
$("#config-unauthenticated-message").hide();
for (let provider of config.data.providers)
if (provider.provider == "GitHub" && provider.token == "")
$("#config-unauthenticated-message").show();
// Replace the global config data with the local config data value, it is assumed that all data was validated at the ui
config.updateFromString(JSON.stringify(data));
configController.updateMainTarget();
this.displayToast("Configuration saved");
},
encryptedMode: function () {
$("#config-encrypt").hide();
$("#config-decrypt").show();

displayToast: function (message) {
$(".toast-body").html(`<strong>${message}</strong>`);
bootstrap.Toast.getOrCreateInstance($('#liveToast')[0]).show();
},
decryptedMode: function () {
$("#config-encrypt").show();
$("#config-decrypt").hide();
}

}

Expand Down
Loading

0 comments on commit d44b4cc

Please sign in to comment.