diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-22 18:08:09 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-22 18:08:09 +0300 |
commit | d6e421b21ed5574700c165cd3361094f4093ac72 (patch) | |
tree | d3c2da7baa477e210c7538bbab1acfa13167411f /app | |
parent | 808c799a67a1cf2489a343a6976f55c74aec398b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/pipelines/components/dag/utils.js | 187 | ||||
-rw-r--r-- | app/controllers/concerns/enforces_two_factor_authentication.rb | 3 | ||||
-rw-r--r-- | app/models/user.rb | 12 |
3 files changed, 190 insertions, 12 deletions
diff --git a/app/assets/javascripts/pipelines/components/dag/utils.js b/app/assets/javascripts/pipelines/components/dag/utils.js new file mode 100644 index 00000000000..20d1f785187 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/dag/utils.js @@ -0,0 +1,187 @@ +import { sankey, sankeyLeft } from 'd3-sankey'; +import { uniqWith, isEqual } from 'lodash'; + +/* + The following functions are the main engine in transforming the data as + received from the endpoint into the format the d3 graph expects. + + Input is of the form: + [stages] + stages: {name, groups} + groups: [{ name, size, jobs }] + name is a group name; in the case that the group has one job, it is + also the job name + size is the number of parallel jobs + jobs: [{ name, needs}] + job name is either the same as the group name or group x/y + + Output is of the form: + { nodes: [node], links: [link] } + node: { name, category }, + unused info passed through + link: { source, target, value }, with source & target being node names + and value being a constant + + We create nodes, create links, and then dedupe the links, so that in the case where + job 4 depends on job 1 and job 2, and job 2 depends on job 1, we show only a single link + from job 1 to job 2 then another from job 2 to job 4. + + CREATE NODES + stage.name -> node.category + stage.group.name -> node.name (this is the group name if there are parallel jobs) + stage.group.jobs -> node.jobs + stage.group.size -> node.size + + CREATE LINKS + stages.groups.name -> target + stages.groups.needs.each -> source (source is the name of the group, not the parallel job) + 10 -> value (constant) + */ + +export const createNodes = data => { + return data.flatMap(({ groups, name }) => { + return groups.map(group => { + return { ...group, category: name }; + }); + }); +}; + +export const createNodeDict = nodes => { + return nodes.reduce((acc, node) => { + const newNode = { + ...node, + needs: node.jobs.map(job => job.needs || []).flat(), + }; + + if (node.size > 1) { + node.jobs.forEach(job => { + acc[job.name] = newNode; + }); + } + + acc[node.name] = newNode; + return acc; + }, {}); +}; + +export const createNodesStructure = data => { + const nodes = createNodes(data); + const nodeDict = createNodeDict(nodes); + + return { nodes, nodeDict }; +}; + +export const makeLinksFromNodes = (nodes, nodeDict) => { + const constantLinkValue = 10; // all links are the same weight + return nodes + .map(group => { + return group.jobs.map(job => { + if (!job.needs) { + return []; + } + + return job.needs.map(needed => { + return { + source: nodeDict[needed]?.name, + target: group.name, + value: constantLinkValue, + }; + }); + }); + }) + .flat(2); +}; + +export const getAllAncestors = (nodes, nodeDict) => { + const needs = nodes + .map(node => { + return nodeDict[node].needs || ''; + }) + .flat() + .filter(Boolean); + + if (needs.length) { + return [...needs, ...getAllAncestors(needs, nodeDict)]; + } + + return []; +}; + +export const filterByAncestors = (links, nodeDict) => + links.filter(({ target, source }) => { + /* + + for every link, check out it's target + for every target, get the target node's needs + then drop the current link source from that list + + call a function to get all ancestors, recursively + is the current link's source in the list of all parents? + then we drop this link + + */ + const targetNode = target; + const targetNodeNeeds = nodeDict[targetNode].needs; + const targetNodeNeedsMinusSource = targetNodeNeeds.filter(need => need !== source); + + const allAncestors = getAllAncestors(targetNodeNeedsMinusSource, nodeDict); + return !allAncestors.includes(source); + }); + +export const parseData = data => { + const { nodes, nodeDict } = createNodesStructure(data); + const allLinks = makeLinksFromNodes(nodes, nodeDict); + const filteredLinks = filterByAncestors(allLinks, nodeDict); + const links = uniqWith(filteredLinks, isEqual); + + return { nodes, links }; +}; + +/* + createSankey calls the d3 layout to generate the relationships and positioning + values for the nodes and links in the graph. + */ + +export const createSankey = ({ width, height, nodeWidth, nodePadding, paddingForLabels }) => { + const sankeyGenerator = sankey() + .nodeId(({ name }) => name) + .nodeAlign(sankeyLeft) + .nodeWidth(nodeWidth) + .nodePadding(nodePadding) + .extent([ + [paddingForLabels, paddingForLabels], + [width - paddingForLabels, height - paddingForLabels], + ]); + return ({ nodes, links }) => + sankeyGenerator({ + nodes: nodes.map(d => ({ ...d })), + links: links.map(d => ({ ...d })), + }); +}; + +/* + The number of nodes in the most populous generation drives the height of the graph. +*/ + +export const getMaxNodes = nodes => { + const counts = nodes.reduce((acc, { layer }) => { + if (!acc[layer]) { + acc[layer] = 0; + } + + acc[layer] += 1; + + return acc; + }, []); + + return Math.max(...counts); +}; + +/* + Because we cannot know if a node is part of a relationship until after we + generate the links with createSankey, this function is used after the first call + to find nodes that have no relations. +*/ + +export const removeOrphanNodes = sankeyfiedNodes => { + return sankeyfiedNodes.filter(node => node.sourceLinks.length || node.targetLinks.length); +}; diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb index d486d734db8..6c443611a60 100644 --- a/app/controllers/concerns/enforces_two_factor_authentication.rb +++ b/app/controllers/concerns/enforces_two_factor_authentication.rb @@ -23,8 +23,7 @@ module EnforcesTwoFactorAuthentication def two_factor_authentication_required? Gitlab::CurrentSettings.require_two_factor_authentication? || - current_user.try(:require_two_factor_authentication_from_group?) || - current_user.try(:ultraauth_user?) + current_user.try(:require_two_factor_authentication_from_group?) end def current_user_requires_two_factor? diff --git a/app/models/user.rb b/app/models/user.rb index 1ba5b9cdf71..021b1e60646 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -954,11 +954,11 @@ class User < ApplicationRecord end def allow_password_authentication_for_web? - Gitlab::CurrentSettings.password_authentication_enabled_for_web? && !ldap_user? && !ultraauth_user? + Gitlab::CurrentSettings.password_authentication_enabled_for_web? && !ldap_user? end def allow_password_authentication_for_git? - Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user? && !ultraauth_user? + Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user? end def can_change_username? @@ -1046,14 +1046,6 @@ class User < ApplicationRecord end end - def ultraauth_user? - if identities.loaded? - identities.find { |identity| Gitlab::Auth::OAuth::Provider.ultraauth_provider?(identity.provider) && !identity.extern_uid.nil? } - else - identities.exists?(["provider = ? AND extern_uid IS NOT NULL", "ultraauth"]) - end - end - def ldap_identity @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"]) end |