Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue2
-rw-r--r--app/assets/javascripts/repository/index.js8
-rw-r--r--app/assets/javascripts/repository/utils/dom.js9
-rw-r--r--app/graphql/mutations/issues/base.rb34
-rw-r--r--app/graphql/mutations/issues/set_due_date.rb27
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/helpers/tree_helper.rb4
-rw-r--r--changelogs/unreleased/36313-graphql-mutation-for-changing-due-date-of-an-issue.yml5
-rw-r--r--changelogs/unreleased/37033-auto-devops-suppress-progress-on-pulling-docker-base-image-to-be-ru.yml5
-rw-r--r--changelogs/unreleased/new-33257-prevent-accidental-deletions-via-soft-delete-for-groups-db-chan.yml5
-rw-r--r--db/migrate/20191118053631_add_group_deletion_schedules.rb28
-rw-r--r--db/schema.rb9
-rw-r--r--doc/README.md4
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql46
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json157
-rw-r--r--doc/api/graphql/reference/index.md8
-rw-r--r--lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml4
-rw-r--r--lib/gitlab/graphql/connections/keyset/connection.rb7
-rw-r--r--lib/gitlab/graphql/connections/keyset/legacy_keyset_connection.rb66
-rw-r--r--spec/frontend/repository/utils/dom_spec.js12
-rw-r--r--spec/graphql/mutations/issues/set_due_date_spec.rb39
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb127
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb61
23 files changed, 461 insertions, 207 deletions
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index afb58a60155..f6b9ea5d30d 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -124,7 +124,7 @@ export default {
},
{
attrs: {
- href: this.newBlobPath,
+ href: `${this.newBlobPath}${this.currentPath}`,
class: 'qa-new-file-option',
},
text: __('New file'),
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index d826f209815..ae6409a0ac9 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -7,6 +7,7 @@ import TreeActionLink from './components/tree_action_link.vue';
import DirectoryDownloadLinks from './components/directory_download_links.vue';
import apolloProvider from './graphql';
import { setTitle } from './utils/title';
+import { updateFormAction } from './utils/dom';
import { parseBoolean } from '../lib/utils/common_utils';
import { webIDEUrl } from '../lib/utils/url_utility';
import { __ } from '../locale';
@@ -42,8 +43,15 @@ export default function setupVueRepositoryList() {
forkNewBlobPath,
forkNewDirectoryPath,
forkUploadBlobPath,
+ uploadPath,
+ newDirPath,
} = breadcrumbEl.dataset;
+ router.afterEach(({ params: { pathMatch = '/' } }) => {
+ updateFormAction('.js-upload-blob-form', uploadPath, pathMatch);
+ updateFormAction('.js-create-dir-form', newDirPath, pathMatch);
+ });
+
// eslint-disable-next-line no-new
new Vue({
el: breadcrumbEl,
diff --git a/app/assets/javascripts/repository/utils/dom.js b/app/assets/javascripts/repository/utils/dom.js
index 963e6fc0bc4..81565a00d82 100644
--- a/app/assets/javascripts/repository/utils/dom.js
+++ b/app/assets/javascripts/repository/utils/dom.js
@@ -1,4 +1,11 @@
-// eslint-disable-next-line import/prefer-default-export
export const updateElementsVisibility = (selector, isVisible) => {
document.querySelectorAll(selector).forEach(elem => elem.classList.toggle('hidden', !isVisible));
};
+
+export const updateFormAction = (selector, basePath, path) => {
+ const form = document.querySelector(selector);
+
+ if (form) {
+ form.action = `${basePath}${path}`;
+ }
+};
diff --git a/app/graphql/mutations/issues/base.rb b/app/graphql/mutations/issues/base.rb
new file mode 100644
index 00000000000..b7fa234a50b
--- /dev/null
+++ b/app/graphql/mutations/issues/base.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class Base < BaseMutation
+ include Mutations::ResolvesProject
+
+ argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: "The project the issue to mutate is in"
+
+ argument :iid, GraphQL::STRING_TYPE,
+ required: true,
+ description: "The iid of the issue to mutate"
+
+ field :issue,
+ Types::IssueType,
+ null: true,
+ description: "The issue after mutation"
+
+ authorize :update_issue
+
+ private
+
+ def find_object(project_path:, iid:)
+ project = resolve_project(full_path: project_path)
+ resolver = Resolvers::IssuesResolver
+ .single.new(object: project, context: context)
+
+ resolver.resolve(iid: iid)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/set_due_date.rb b/app/graphql/mutations/issues/set_due_date.rb
new file mode 100644
index 00000000000..1855c6f053b
--- /dev/null
+++ b/app/graphql/mutations/issues/set_due_date.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class SetDueDate < Base
+ graphql_name 'IssueSetDueDate'
+
+ argument :due_date,
+ Types::TimeType,
+ required: true,
+ description: 'The desired due date for the issue'
+
+ def resolve(project_path:, iid:, due_date:)
+ issue = authorized_find!(project_path: project_path, iid: iid)
+ project = issue.project
+
+ ::Issues::UpdateService.new(project, current_user, due_date: due_date)
+ .execute(issue)
+
+ {
+ issue: issue,
+ errors: issue.errors.full_messages
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 2408dc7fd1b..ecdbba477d7 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -9,6 +9,7 @@ module Types
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
+ mount_mutation Mutations::Issues::SetDueDate
mount_mutation Mutations::MergeRequests::SetLabels
mount_mutation Mutations::MergeRequests::SetLocked
mount_mutation Mutations::MergeRequests::SetMilestone
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index fc25b78da93..af1919eeb40 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -158,7 +158,9 @@ module TreeHelper
def breadcrumb_data_attributes
attrs = {
can_collaborate: can_collaborate_with_project?(@project).to_s,
- new_blob_path: project_new_blob_path(@project, @id),
+ new_blob_path: project_new_blob_path(@project, @ref),
+ upload_path: project_create_blob_path(@project, @ref),
+ new_dir_path: project_create_dir_path(@project, @ref),
new_branch_path: new_project_branch_path(@project),
new_tag_path: new_project_tag_path(@project),
can_edit_tree: can_edit_tree?.to_s
diff --git a/changelogs/unreleased/36313-graphql-mutation-for-changing-due-date-of-an-issue.yml b/changelogs/unreleased/36313-graphql-mutation-for-changing-due-date-of-an-issue.yml
new file mode 100644
index 00000000000..f63605bd593
--- /dev/null
+++ b/changelogs/unreleased/36313-graphql-mutation-for-changing-due-date-of-an-issue.yml
@@ -0,0 +1,5 @@
+---
+title: Add GraphQL mutation for changing due date of an issue
+merge_request: 20577
+author:
+type: added
diff --git a/changelogs/unreleased/37033-auto-devops-suppress-progress-on-pulling-docker-base-image-to-be-ru.yml b/changelogs/unreleased/37033-auto-devops-suppress-progress-on-pulling-docker-base-image-to-be-ru.yml
new file mode 100644
index 00000000000..dbaa3ec44be
--- /dev/null
+++ b/changelogs/unreleased/37033-auto-devops-suppress-progress-on-pulling-docker-base-image-to-be-ru.yml
@@ -0,0 +1,5 @@
+---
+title: Suppress progress on pulling image on Code Quality of Auto DevOps
+merge_request: 20604
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/new-33257-prevent-accidental-deletions-via-soft-delete-for-groups-db-chan.yml b/changelogs/unreleased/new-33257-prevent-accidental-deletions-via-soft-delete-for-groups-db-chan.yml
new file mode 100644
index 00000000000..6b300dd53f1
--- /dev/null
+++ b/changelogs/unreleased/new-33257-prevent-accidental-deletions-via-soft-delete-for-groups-db-chan.yml
@@ -0,0 +1,5 @@
+---
+title: Add migrations for 'soft-delete for groups' feature
+merge_request: 20276
+author:
+type: added
diff --git a/db/migrate/20191118053631_add_group_deletion_schedules.rb b/db/migrate/20191118053631_add_group_deletion_schedules.rb
new file mode 100644
index 00000000000..6f3ed27e156
--- /dev/null
+++ b/db/migrate/20191118053631_add_group_deletion_schedules.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class AddGroupDeletionSchedules < ActiveRecord::Migration[5.2]
+ DOWNTIME = false
+
+ def up
+ create_table :group_deletion_schedules, id: false do |t|
+ t.references :group,
+ foreign_key: { on_delete: :cascade, to_table: :namespaces },
+ default: nil,
+ index: false,
+ primary_key: true
+
+ t.references :user,
+ index: true,
+ foreign_key: { on_delete: :nullify },
+ null: false
+
+ t.date :marked_for_deletion_on,
+ index: true,
+ null: false
+ end
+ end
+
+ def down
+ drop_table :group_deletion_schedules
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b31ed03a34f..8c8b48f0bbe 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1892,6 +1892,13 @@ ActiveRecord::Schema.define(version: 2019_11_19_023952) do
t.index ["key", "value"], name: "index_group_custom_attributes_on_key_and_value"
end
+ create_table "group_deletion_schedules", primary_key: "group_id", id: :bigint, default: nil, force: :cascade do |t|
+ t.bigint "user_id", null: false
+ t.date "marked_for_deletion_on", null: false
+ t.index ["marked_for_deletion_on"], name: "index_group_deletion_schedules_on_marked_for_deletion_on"
+ t.index ["user_id"], name: "index_group_deletion_schedules_on_user_id"
+ end
+
create_table "group_group_links", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -4413,6 +4420,8 @@ ActiveRecord::Schema.define(version: 2019_11_19_023952) do
add_foreign_key "gpg_signatures", "projects", on_delete: :cascade
add_foreign_key "grafana_integrations", "projects", on_delete: :cascade
add_foreign_key "group_custom_attributes", "namespaces", column: "group_id", on_delete: :cascade
+ add_foreign_key "group_deletion_schedules", "namespaces", column: "group_id", on_delete: :cascade
+ add_foreign_key "group_deletion_schedules", "users", on_delete: :nullify
add_foreign_key "group_group_links", "namespaces", column: "shared_group_id", on_delete: :cascade
add_foreign_key "group_group_links", "namespaces", column: "shared_with_group_id", on_delete: :cascade
add_foreign_key "identities", "saml_providers", name: "fk_aade90f0fc", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index c8438f5fb8b..fb343ae91bb 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -23,7 +23,7 @@ No matter how you use GitLab, we have documentation for you.
| Essential Documentation | Essential Documentation |
|:-------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------|
| [**User Documentation**](user/index.md)<br/>Discover features and concepts for GitLab users. | [**Administrator documentation**](administration/index.md)<br/>Everything GitLab self-managed administrators need to know. |
-| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](#new-to-git-and-gitlab)<br/>We have resources to get you started. |
+| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](#new-to-git-and-gitlab)<br/>We have the resources to get you started. |
| [**Building an integration with GitLab?**](#building-an-integration-with-gitlab)<br/>Consult our automation and integration documentation. | [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Consult our handy guides. |
| [**Install GitLab**](https://about.gitlab.com/install/)<br/>Installation options for different platforms. | [**Customers**](subscriptions/index.md)<br/>Information for new and existing customers. |
| [**Update GitLab**](update/README.md)<br/>Update your GitLab self-managed instance to the latest version. | [**GitLab Releases**](https://about.gitlab.com/releases/)<br/>What's new in GitLab. |
@@ -412,7 +412,7 @@ Learn more about using Git, and using Git with GitLab:
| Topic | Description |
|:----------------------------------------------------------------------------|:---------------------------------------------------------------------------|
| [Git](topics/git/index.md) | Getting started with Git, branching strategies, Git LFS, and advanced use. |
-| [Git cheatsheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF describing the most used Git operations. |
+| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF describing the most used Git operations. |
| [GitLab Flow](topics/gitlab_flow.md) | Explore the best of Git with the GitLab Flow strategy. |
<div align="right">
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 42446fb6ce1..d8902be92eb 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -2519,6 +2519,51 @@ type IssuePermissions {
}
"""
+Autogenerated input type of IssueSetDueDate
+"""
+input IssueSetDueDateInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The desired due date for the issue
+ """
+ dueDate: Time!
+
+ """
+ The iid of the issue to mutate
+ """
+ iid: String!
+
+ """
+ The project the issue to mutate is in
+ """
+ projectPath: ID!
+}
+
+"""
+Autogenerated return type of IssueSetDueDate
+"""
+type IssueSetDueDatePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Reasons why the mutation failed.
+ """
+ errors: [String!]!
+
+ """
+ The issue after mutation
+ """
+ issue: Issue
+}
+
+"""
Values for sorting issues
"""
enum IssueSort {
@@ -3511,6 +3556,7 @@ type Mutation {
destroyNote(input: DestroyNoteInput!): DestroyNotePayload
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
+ issueSetDueDate(input: IssueSetDueDateInput!): IssueSetDueDatePayload
mergeRequestSetAssignees(input: MergeRequestSetAssigneesInput!): MergeRequestSetAssigneesPayload
mergeRequestSetLabels(input: MergeRequestSetLabelsInput!): MergeRequestSetLabelsPayload
mergeRequestSetLocked(input: MergeRequestSetLockedInput!): MergeRequestSetLockedPayload
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 07b91c1af1a..fba3dcca14d 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -14113,6 +14113,33 @@
"deprecationReason": null
},
{
+ "name": "issueSetDueDate",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "IssueSetDueDateInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IssueSetDueDatePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "mergeRequestSetAssignees",
"description": null,
"args": [
@@ -14960,6 +14987,136 @@
},
{
"kind": "OBJECT",
+ "name": "IssueSetDueDatePayload",
+ "description": "Autogenerated return type of IssueSetDueDate",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Reasons why the mutation failed.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "issue",
+ "description": "The issue after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Issue",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "IssueSetDueDateInput",
+ "description": "Autogenerated input type of IssueSetDueDate",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project the issue to mutate is in",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "iid",
+ "description": "The iid of the issue to mutate",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "dueDate",
+ "description": "The desired due date for the issue",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "MergeRequestSetLabelsPayload",
"description": "Autogenerated return type of MergeRequestSetLabels",
"fields": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 18d49c9a7e1..4b71c9a8eaf 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -375,6 +375,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| `createDesign` | Boolean! | Whether or not a user can perform `create_design` on this resource |
| `destroyDesign` | Boolean! | Whether or not a user can perform `destroy_design` on this resource |
+### IssueSetDueDatePayload
+
+| Name | Type | Description |
+| --- | ---- | ---------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Reasons why the mutation failed. |
+| `issue` | Issue | The issue after mutation |
+
### Label
| Name | Type | Description |
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index a60b00b2ee8..8f50f38bbed 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -7,6 +7,7 @@ code_quality:
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
+ CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/security-products/codequality:12-5-stable"
script:
- |
if ! docker info &>/dev/null; then
@@ -14,11 +15,12 @@ code_quality:
export DOCKER_HOST='tcp://localhost:2375'
fi
fi
+ - docker pull --quiet "$CODE_QUALITY_IMAGE"
- docker run
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
- "registry.gitlab.com/gitlab-org/security-products/codequality:12-5-stable" /code
+ "$CODE_QUALITY_IMAGE" /code
artifacts:
reports:
codequality: gl-code-quality-report.json
diff --git a/lib/gitlab/graphql/connections/keyset/connection.rb b/lib/gitlab/graphql/connections/keyset/connection.rb
index e42a705a78a..5de075f2f7a 100644
--- a/lib/gitlab/graphql/connections/keyset/connection.rb
+++ b/lib/gitlab/graphql/connections/keyset/connection.rb
@@ -32,18 +32,11 @@ module Gitlab
class Connection < GraphQL::Relay::BaseConnection
include Gitlab::Utils::StrongMemoize
- # TODO https://gitlab.com/gitlab-org/gitlab/issues/35104
- include Gitlab::Graphql::Connections::Keyset::LegacyKeysetConnection
-
def cursor_from_node(node)
- return legacy_cursor_from_node(node) if use_legacy_pagination?
-
encoded_json_from_ordering(node)
end
def sliced_nodes
- return legacy_sliced_nodes if use_legacy_pagination?
-
@sliced_nodes ||=
begin
OrderInfo.validate_ordering(ordered_nodes, order_list)
diff --git a/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection.rb b/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection.rb
deleted file mode 100644
index baf900d1048..00000000000
--- a/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-# TODO https://gitlab.com/gitlab-org/gitlab/issues/35104
-module Gitlab
- module Graphql
- module Connections
- module Keyset
- module LegacyKeysetConnection
- def legacy_cursor_from_node(node)
- encode(node[legacy_order_field].to_s)
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def legacy_sliced_nodes
- @sliced_nodes ||=
- begin
- sliced = nodes
-
- sliced = sliced.where(legacy_before_slice) if before.present?
- sliced = sliced.where(legacy_after_slice) if after.present?
-
- sliced
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- private
-
- def use_legacy_pagination?
- strong_memoize(:feature_disabled) do
- Feature.disabled?(:graphql_keyset_pagination, default_enabled: true)
- end
- end
-
- def legacy_before_slice
- if legacy_sort_direction == :asc
- arel_table[legacy_order_field].lt(decode(before))
- else
- arel_table[legacy_order_field].gt(decode(before))
- end
- end
-
- def legacy_after_slice
- if legacy_sort_direction == :asc
- arel_table[legacy_order_field].gt(decode(after))
- else
- arel_table[legacy_order_field].lt(decode(after))
- end
- end
-
- def legacy_order_info
- @legacy_order_info ||= nodes.order_values.first
- end
-
- def legacy_order_field
- @legacy_order_field ||= legacy_order_info&.expr&.name || nodes.primary_key
- end
-
- def legacy_sort_direction
- @legacy_order_direction ||= legacy_order_info&.direction || :desc
- end
- end
- end
- end
- end
-end
diff --git a/spec/frontend/repository/utils/dom_spec.js b/spec/frontend/repository/utils/dom_spec.js
index 678d444904d..bf98a9e1a4d 100644
--- a/spec/frontend/repository/utils/dom_spec.js
+++ b/spec/frontend/repository/utils/dom_spec.js
@@ -1,5 +1,5 @@
import { setHTMLFixture } from '../../helpers/fixtures';
-import { updateElementsVisibility } from '~/repository/utils/dom';
+import { updateElementsVisibility, updateFormAction } from '~/repository/utils/dom';
describe('updateElementsVisibility', () => {
it('adds hidden class', () => {
@@ -18,3 +18,13 @@ describe('updateElementsVisibility', () => {
expect(document.querySelector('.js-test').classList).not.toContain('hidden');
});
});
+
+describe('updateFormAction', () => {
+ it('updates form action', () => {
+ setHTMLFixture('<form class="js-test" action="/"></form>');
+
+ updateFormAction('.js-test', '/gitlab/create', '/test');
+
+ expect(document.querySelector('.js-test').action).toBe('http://localhost/gitlab/create/test');
+ });
+});
diff --git a/spec/graphql/mutations/issues/set_due_date_spec.rb b/spec/graphql/mutations/issues/set_due_date_spec.rb
new file mode 100644
index 00000000000..9a1f0925fe3
--- /dev/null
+++ b/spec/graphql/mutations/issues/set_due_date_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mutations::Issues::SetDueDate do
+ let(:issue) { create(:issue) }
+ let(:user) { create(:user) }
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
+
+ describe '#resolve' do
+ let(:due_date) { 2.days.since }
+ let(:mutated_issue) { subject[:issue] }
+ subject { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, due_date: due_date) }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when the user can update the issue' do
+ before do
+ issue.project.add_developer(user)
+ end
+
+ it 'returns the issue with updated due date' do
+ expect(mutated_issue).to eq(issue)
+ expect(mutated_issue.due_date).to eq(Date.today + 2.days)
+ expect(subject[:errors]).to be_empty
+ end
+
+ context 'when passing incorrect due date value' do
+ let(:due_date) { 'test' }
+
+ it 'does not update due date' do
+ expect(mutated_issue.due_date).to eq(issue.due_date)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb
deleted file mode 100644
index aaf28fed684..00000000000
--- a/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-# frozen_string_literal: true
-
-# TODO https://gitlab.com/gitlab-org/gitlab/issues/35104
-require 'spec_helper'
-
-describe Gitlab::Graphql::Connections::Keyset::LegacyKeysetConnection do
- describe 'old keyset_connection' do
- let(:described_class) { Gitlab::Graphql::Connections::Keyset::Connection }
- let(:nodes) { Project.all.order(id: :asc) }
- let(:arguments) { {} }
- subject(:connection) do
- described_class.new(nodes, arguments, max_page_size: 3)
- end
-
- before do
- stub_feature_flags(graphql_keyset_pagination: false)
- end
-
- def encoded_property(value)
- Base64Bp.urlsafe_encode64(value.to_s, padding: false)
- end
-
- describe '#cursor_from_nodes' do
- let(:project) { create(:project) }
-
- it 'returns an encoded ID' do
- expect(connection.cursor_from_node(project))
- .to eq(encoded_property(project.id))
- end
-
- context 'when an order was specified' do
- let(:nodes) { Project.order(:updated_at) }
-
- it 'returns the encoded value of the order' do
- expect(connection.cursor_from_node(project))
- .to eq(encoded_property(project.updated_at))
- end
- end
- end
-
- describe '#sliced_nodes' do
- let(:projects) { create_list(:project, 4) }
-
- context 'when before is passed' do
- let(:arguments) { { before: encoded_property(projects[1].id) } }
-
- it 'only returns the project before the selected one' do
- expect(subject.sliced_nodes).to contain_exactly(projects.first)
- end
-
- context 'when the sort order is descending' do
- let(:nodes) { Project.all.order(id: :desc) }
-
- it 'returns the correct nodes' do
- expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
- end
- end
- end
-
- context 'when after is passed' do
- let(:arguments) { { after: encoded_property(projects[1].id) } }
-
- it 'only returns the project before the selected one' do
- expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
- end
-
- context 'when the sort order is descending' do
- let(:nodes) { Project.all.order(id: :desc) }
-
- it 'returns the correct nodes' do
- expect(subject.sliced_nodes).to contain_exactly(projects.first)
- end
- end
- end
-
- context 'when both before and after are passed' do
- let(:arguments) do
- {
- after: encoded_property(projects[1].id),
- before: encoded_property(projects[3].id)
- }
- end
-
- it 'returns the expected set' do
- expect(subject.sliced_nodes).to contain_exactly(projects[2])
- end
- end
- end
-
- describe '#paged_nodes' do
- let!(:projects) { create_list(:project, 5) }
-
- it 'returns the collection limited to max page size' do
- expect(subject.paged_nodes.size).to eq(3)
- end
-
- it 'is a loaded memoized array' do
- expect(subject.paged_nodes).to be_an(Array)
- expect(subject.paged_nodes.object_id).to eq(subject.paged_nodes.object_id)
- end
-
- context 'when `first` is passed' do
- let(:arguments) { { first: 2 } }
-
- it 'returns only the first elements' do
- expect(subject.paged_nodes).to contain_exactly(projects.first, projects.second)
- end
- end
-
- context 'when `last` is passed' do
- let(:arguments) { { last: 2 } }
-
- it 'returns only the last elements' do
- expect(subject.paged_nodes).to contain_exactly(projects[3], projects[4])
- end
- end
-
- context 'when both are passed' do
- let(:arguments) { { first: 2, last: 2 } }
-
- it 'raises an error' do
- expect { subject.paged_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
- end
- end
- end
- end
-end
diff --git a/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb b/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb
new file mode 100644
index 00000000000..1efa9e16233
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Setting Due Date of an issue' do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+ let(:issue) { create(:issue) }
+ let(:project) { issue.project }
+ let(:input) { { due_date: 2.days.since } }
+
+ let(:mutation) do
+ variables = {
+ project_path: project.full_path,
+ iid: issue.iid.to_s
+ }
+ graphql_mutation(:issue_set_due_date, variables.merge(input),
+ <<-QL.strip_heredoc
+ clientMutationId
+ errors
+ issue {
+ iid
+ dueDate
+ }
+ QL
+ )
+ end
+
+ def mutation_response
+ graphql_mutation_response(:issue_set_due_date)
+ end
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'returns an error if the user is not allowed to update the issue' do
+ error = "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+ post_graphql_mutation(mutation, current_user: create(:user))
+
+ expect(graphql_errors).to include(a_hash_including('message' => error))
+ end
+
+ it 'updates the issue due date' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['issue']['dueDate']).to eq(2.days.since.to_date.to_s)
+ end
+
+ context 'when passing due date without a date value' do
+ let(:input) { { due_date: 'test' } }
+
+ it 'returns internal server error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_errors).to include(a_hash_including('message' => 'Internal server error'))
+ end
+ end
+end