Geo Replicables List: Add ability to find a record by ID in UI

Why are we doing this work

note: This may need to be implemented via multiple MRs.

The list view is currently going through a migration and may look different depending on if you have the feature flag geo_replicables_filtered_list_view and/or geo_replicables_show_view enabled.

We are working to enhance the functionality available in the Geo Replicables List View &16585 (closed)

Currently, there is no way for a customer to do the following:

Find an exact replication record on demand in the UI

Typically, we solve this issue in list views by adding search-ability. Searching is a very complicated subject inside of Geo due to how Geo Registries are stored. This change proposing a middle ground of providing a way to search by ID to return a single record if it exists.

Why this is NOT a search

Searching comes with expectations of more robust functionality. We have learned in the past that overpromising on search functionality can really be harmful to the trust customers have in a feature.

More can be read about this over on Geo: Support Filtering Replicables by Keyword (#411770 - closed)

Rather I am proposing we find a middle ground where we clearly communicate through the UI to customers that this a find by ID function rather than a search. Allow customers who know exactly what they are looking for to directly find it without having to sift through pages of records.

Considerations

With the upcoming UI changes we are going to be using FilteredSearch. The intent is the user will be able to freeform search for a Registry via Registry ID. We can build a semi-intelligent mechanism that can attempt to interpret the search and then Generate the GID to form a proper API query.

Proposal Demo

Screen_Recording_2025-04-10_at_1.58.04_PM

Relevant links

Implementation plan

  1. Add the ability to manage the ids in the vuex store (geo_replicable/store)
// state.js

idsFilter: '',
// mutation_types.js

export const SET_IDS_FILTER = 'SET_IDS_FILTER';
// mutations.js

[types.SET_IDS_FILTER](state, filter) {
  state.idsFilter = filter;
},
  1. Add ability when setting the ids filter in the vuex store to accept either the gid or registryId
// actions.js

export const setIdsFilter = ({ commit }, { ids, graphqlRegistryClass }) => {
  const filter = ids.split(' ').map(id => {
    if (isGid(id)) {
      return id
    }

    try {
      const num = id.replace(`${graphqlRegistryClass}/`, '')
      return convertToGraphQLId(graphqlRegistryClass, num)
    } catch {
      return null
    }
  })

  commit(types.SET_IDS_FILTER, filter);
};
  1. Add ability to add the ids to the GraphQL filter replicable_type_query_builder.js
// replicable_type_query_builder.js
// some of file omitted for clairty

export default (graphQlFieldName, verificationEnabled) => {
  return gql`
    query($first: Int, $last: Int, $before: String!, $after: String!, $replicationState: ReplicationStateEnum, $ids: [ID!]) {
      geoNode {
        ${graphQlFieldName}(first: $first, last: $last, before: $before, after: $after, replicationState: $replicationState, ids: $ids) {
      }
    }
  `
}
// actions.js
// some of file omitted for clarity

export const fetchReplicableItems = ({ state, dispatch }, direction) => {
  // ...
  const ids = state.idsFilter || null;

  client
    .query({
      query: buildReplicableTypeQuery(state.graphqlFieldName, state.verificationEnabled),
      variables: { first, last, before, after, replicationState, ids },
    })
}
  1. Add support for the ids as a token in geo_replicable/constants.js
// some of file omitted for clarity
// constants.js

export const TOKEN_TYPES = {
  // ...
  IDS: 'ids'
};
  1. Add support for the ids as a filter in geo_replicable/filters.js
// some of file omitted for clarity
// filters.js

export const processFilters = (filters) => {
  // ...

  // ids is stored as plaintext in the filtered search
  if (typeof filter === 'string') {
    query[TOKEN_TYPES.IDS] = filter
  }

  return { query, url };
};
  1. Add support to get the ids from the query via getFiltersFromQuery() in app.vue
// some of file omitted for clarity
// app.vue

getFiltersFromQuery() {
  // ...
  const { ids } = queryToObject(window.location.search || '');

  // IDs filter is treated as plaintext and should be last filter in array
  if (ids) {
    filters.push(ids)
    this.setIdsFilter({ ids, graphqlRegistryClass: this.graphqlRegistryClass })
  }
},
  1. Update search-text-option-label to communicate it is a freeform search by id in geo_replicable_filtered_search.vue
// some of file omitted for clarity
// geo_replicable_filtered_search.vue

<script>
  export default {
    i18n: {
      searchById: __('Search by ID')
    }
  }
</script>

<template>
  <gl-filtered-search
    :search-text-option-label="$options.i18n.searchById"
  />
</template>
Edited by 🤖 GitLab Bot 🤖