mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-02 19:47:54 +00:00 
			
		
		
		
	* Ember Engine Setup for Secrets Sync (#23653) * ember engine setup for secrets sync * Update ui/lib/sync/addon/routes.js Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> --------- Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> * Sync Mirage Setup (#23683) * adds mirage setup for sync endpoints * updates secret_name default in sync-association mirage factory * UI Secrets Sync: Ember data sync destinations (#23674) * add models * adapters * base model adapter * update test response * add sync destinations helper * finish renaming base destination model/adapter * add comment * add serializer * use normalizeItems instead * destination serializer test * add destination find method; * add conditional operand * UI Secrets Sync: Overview landing page (#23696) * add models * adapters * base model adapter * update test response * add sync destinations helper * finish renaming base destination model/adapter * add comment * add serializer * doc-link helper * add version service * landing and overview component * overview page * add tests * UI Secrets Sync: Destinations adapter add LIST (#23716) * add models * adapters * base model adapter * update test response * add sync destinations helper * finish renaming base destination model/adapter * add comment * add serializer * doc-link helper * add version service * landing and overview component * overview page * build out serializer and adapters * update mirage * fix merge conflicts * one more conflict! * pull transformQueryResponse to separate method in adapter * move data transforming all to serializer and tests * add note to paginationd ocs docs * conditionally render CTA * add lazyPaginatedQuery method to destinations route * remove partial error * Secrets Sync: Destinations create - select type (#23792) * add category to destinations * build select type page * refactor prompt config situation * routing for destinations * update select-type routing * make card width fixed * revert CTA routing change, keep shouldRenderOverview * add header for gif demo to form * cleanup scope * more scope cleanup * add test * add type selector * rename components * rename again * remove async * fix tests * fix select type rename in test * delete renamed test * fix import of general selectors * rename using component syntax * UI Secrets Sync: Create destination form and route (#23806) * add model attribute metadata * add form and save url, remove name and type from serializer * move checkbox list to form field helper * add styling to alert inline * use newly made class * fix cancel action and cleanup form * change quotes * remove checkbox action from form component * add tests * address feedback * add API error test * use create record method instead * adapter test for create record * return from find method if type is undefined * cleanup test selectors * secrets sync: refactor sync destinations helper (#23839) * refactor getter in base destination model * add getters back to model * Secrets sync UI: Destination details page (#23842) * change labels to match params * add maskedParams to base model * add details route * add details view; * update mirage * fix secrets sync link; * delete parent destination route * add copyright header * add secrets route * move sync route outside of secrets/ route * upate mirage * export to-label * finish tests * make ternary * rename header tabs * fix selector in test * Secrets Sync UI: Cleanup headers + tabs (#23873) * remove destination header component, add headers/tabs to all routes * fix header padding * move tabs + toolbar back into component... * add copyright header * add delete modal * lol revert again * add extra line after copyright header * Secrets Sync Destinations List View (#23949) * adds route and page component for sync destinations list view * filters by type first for sync destinations * adds test for store.filterData method * Update ui/app/services/store.js Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com> * updates nav link label for secrets sync * moves sync destinations types out of app-types * moves loading-dropdown-option component to core addon and adds to destination list item menu * change true assertion to deepEqual in sync destinations test * adds copyright header to sync-destinations type file * clear store dataset on sync destination create --------- Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com> * Sync Destinations Capabilities (#23953) * adds route and page component for sync destinations list view * filters by type first for sync destinations * adds test for store.filterData method * adds capabilities checks for sync destinations * removes canList from sync destinations capabilities * updates sync header tests * Update ui/tests/integration/components/sync/sync-header-test.js Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> * updates sync destination response serialization * updates sync destination serializer test * updates sync destinations page test assertions * fixes mirage sync destinations payload issue * removes commented out method in sync destination adapter * fixes inconsistencies with url generation for sync destinations delete * fixes sync destinations page test --------- Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> * Sync Associations Ember Data Setup (#24132) * adds model, adapter and serializer for sync associations * updates sync association adapter save methods to use adapterOptions to determine action * Sync Destination Secrets Route and Page Component (#24155) * renames sync destination header component and adds tests * adds destination secrets route and page component * adds setup-models helper for sync testing * moves destination details test into subdir * adds destination secrets page component tests * adds controller for destination secrets route * fixes pagination route on destination secrets view * fixes sync association updated_at assertion based on timezone * updates kv secret details external route name * updates usage of old spacing style variable after merge * use confirm action instead of contextual confirm (old) component (#24189) * UI Secrets Sync: Adds secret status to kv v2 details page (#24208) * woops! missed this styling for confirm action swap * update link to go to destination secrets * change edit to view secret from destination secrets list * add synDestination to external routes for kv engine * add sync status badge component * export from addon * splaattributes * poll sync status for kv secret details and render * move from controller to component * update name to new destinationName key * reorder list view items * add refresh button * add mirage data * change to loading static * update icons to be sync specific * change name * move button and change fetch to concurrency task * add tests to kv details * add color assertion * add copyright header * small test tweaks * Update ui/tests/integration/components/sync-status-badge-test.js * fixes test --------- Co-authored-by: Jordan Reimer <zofskeez@gmail.com> * Sync Secrets to Destination (#24247) * fixes issue with filter-input debounce and updates to spread attributes for input rather than use args * adds destination sync page component * removes unused var in sync component * adds test for manual mount path input in sync view * updates mount filtering in destinations sync page to target kv v2 * Secrets Sync Landing Page Images (#24277) * updates sync landing page to add marketing images * removes top margin from sync landing-cta * adds aria-describedby to sync landing images * UI Secrets Sync: Serialize trailing slash from destination type (#24294) * remove trailing slash from type in destination LIST response * update keys in mirage and tests * Sync Overview (#24340) * updates landing-cta image to png with matching height * adds ts definitons for sync adapters * updates sync adapters and serializers to add methods for fetching overview data * adds sync associations list handler to mirage and seeds more associations in scenario * adds table and totals cards to sync overview page * adds sync overview page component tests * fixes tests * changes lastSync key to lastUpdated for sync fetchByDestinations response * adds emdash as placeholder for lastUpdated null value in secrets by destination table * updates to handle 0 associations state for destination in overview table * Secrets Sync UI: Add loading and error substates (#24353) * add error substate * add loading substates * delete loading from secrets route * Remove is-version Helper (#24388) * removes is-version helper and injects service into components * updates sync tests using version service to new API * adds comment back for tracked property in secret detials page component * updates sync tests to use common selectors (#24397) * update capitalization to consistently be titlecase, fix breadcrumb selector * clears sync associations from store on destination sync page component destroy (#24450) * KV Suggestion Input (#24447) * updates filter-input component to conditionally show search icon * adds kv-suggestion-input component to core addon * updates destination sync page component to use KvSuggestionInput component * fixes issue in kv-suggestion-input where a partial search term was not replaced with the selected suggestion value * updates kv-suggestion-input to retain focus on suggestion click * fixes test * updates kv-suggestion-input to conditionally render label component * adds comments to kv-suggestion-input regarding trigger * moves alert banner in sync page below button set * moves inputId from getter to class property on kv-suggestion-input * Secrets Sync UI: Editing a destination (#24413) * add form field groups to sync models * update create-and-edit form to use confirmLeave and enableInput component * enable input component * add more stars * update css comments * Update ui/app/styles/helper-classes/flexbox-and-grid.scss * make attrOptions optional * remove decorator * add env variables to subtexr * add subtext to textfile * fix overviwe transition bug * remove breadcrumbs to getter * WIP adapter update * update mirage response * add update method with PATCH * add patch to application adapter * fix typo * finish tests * remove validations because could use environment variables * use getter and setter in model * move update record business to serializer * rest of logic in serializer; gp ; gp * add model validation warnings * cleanup getters * pull create/update logic into method for mirage * add test for validation warning * update KV copy * Sync Success Banner (#24491) * adds success banner to destination sync page * move submit disabled logic to getter in destination sync page * adds id and for attributes to kv mount input in sync page * hides sync success banner on submit * use Sync secrets everywhere (remove new) (#24494) * use Sync secrets everywhere (remove new) * revert test name change * Sync Destinations List Filter Bug (#24496) * fixes issues filtering destinations list * adds test * fixes Sync now action text alignment in destination secrets list * UI Secrets sync: Add purge query param to delete endpoint (#24497) * adds updated_at to mirage set association handler * adds changelog entry * add enterprise in parenthesis for changelog * addres a11y feedback --------- Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Co-authored-by: clairebontempo@gmail.com <clairebontempo@gmail.com> Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com>
		
			
				
	
	
		
			240 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * Copyright (c) HashiCorp, Inc.
 | 
						|
 * SPDX-License-Identifier: BUSL-1.1
 | 
						|
 */
 | 
						|
 | 
						|
import Store from '@ember-data/store';
 | 
						|
import { run, schedule } from '@ember/runloop';
 | 
						|
import { resolve, Promise } from 'rsvp';
 | 
						|
import { dasherize } from '@ember/string';
 | 
						|
import { assert } from '@ember/debug';
 | 
						|
import { set, get } from '@ember/object';
 | 
						|
import clamp from 'vault/utils/clamp';
 | 
						|
import config from 'vault/config/environment';
 | 
						|
import sortObjects from 'vault/utils/sort-objects';
 | 
						|
 | 
						|
const { DEFAULT_PAGE_SIZE } = config.APP;
 | 
						|
 | 
						|
export function normalizeModelName(modelName) {
 | 
						|
  return dasherize(modelName);
 | 
						|
}
 | 
						|
 | 
						|
export function keyForCache(query) {
 | 
						|
  /*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/
 | 
						|
  // we want to ignore size, page, responsePath, and pageFilter in the cacheKey
 | 
						|
  const { size, page, responsePath, pageFilter, ...queryForCache } = query;
 | 
						|
  const cacheKeyObject = Object.keys(queryForCache)
 | 
						|
    .sort()
 | 
						|
    .reduce((result, key) => {
 | 
						|
      result[key] = queryForCache[key];
 | 
						|
      return result;
 | 
						|
    }, {});
 | 
						|
  return JSON.stringify(cacheKeyObject);
 | 
						|
}
 | 
						|
 | 
						|
export default class StoreService extends Store {
 | 
						|
  lazyCaches = new Map();
 | 
						|
 | 
						|
  setLazyCacheForModel(modelName, key, value) {
 | 
						|
    const cacheKey = keyForCache(key);
 | 
						|
    const cache = this.lazyCacheForModel(modelName) || new Map();
 | 
						|
    cache.set(cacheKey, value);
 | 
						|
    const modelKey = normalizeModelName(modelName);
 | 
						|
    this.lazyCaches.set(modelKey, cache);
 | 
						|
  }
 | 
						|
 | 
						|
  getLazyCacheForModel(modelName, key) {
 | 
						|
    const cacheKey = keyForCache(key);
 | 
						|
    const modelCache = this.lazyCacheForModel(modelName);
 | 
						|
    if (modelCache) {
 | 
						|
      return modelCache.get(cacheKey);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  lazyCacheForModel(modelName) {
 | 
						|
    return this.lazyCaches.get(normalizeModelName(modelName));
 | 
						|
  }
 | 
						|
 | 
						|
  // This is the public interface for the store extension - to be used just
 | 
						|
  // like `Store.query`. Special handling of the response is controlled by
 | 
						|
  // `query.pageFilter`, `query.page`, and `query.size`.
 | 
						|
 | 
						|
  // Required attributes of the `query` argument are:
 | 
						|
  //   responsePath: a string indicating the location on the response where
 | 
						|
  //     the array of items will be found
 | 
						|
  //   page: the page number to return
 | 
						|
  //   size: the size of the page
 | 
						|
  //   pageFilter: a string that will be used to do a fuzzy match against the results,
 | 
						|
  //     OR a function to be executed that will receive the dataset as the lone arg.
 | 
						|
  //     Filter is done pre-pagination.
 | 
						|
  lazyPaginatedQuery(modelType, query, adapterOptions) {
 | 
						|
    const skipCache = query.skipCache;
 | 
						|
    // We don't want skipCache to be part of the actual query key, so remove it
 | 
						|
    delete query.skipCache;
 | 
						|
    const adapter = this.adapterFor(modelType);
 | 
						|
    const modelName = normalizeModelName(modelType);
 | 
						|
    const dataCache = skipCache ? this.clearDataset(modelName) : this.getDataset(modelName, query);
 | 
						|
    const responsePath = query.responsePath;
 | 
						|
    assert('responsePath is required', responsePath);
 | 
						|
    assert('page is required', typeof query.page === 'number');
 | 
						|
    if (!query.size) {
 | 
						|
      query.size = DEFAULT_PAGE_SIZE;
 | 
						|
    }
 | 
						|
 | 
						|
    if (dataCache) {
 | 
						|
      return resolve(this.fetchPage(modelName, query));
 | 
						|
    }
 | 
						|
    return adapter
 | 
						|
      .query(this, { modelName }, query, null, adapterOptions)
 | 
						|
      .then((response) => {
 | 
						|
        const serializer = this.serializerFor(modelName);
 | 
						|
        const datasetHelper = serializer.extractLazyPaginatedData;
 | 
						|
        const dataset = datasetHelper
 | 
						|
          ? datasetHelper.call(serializer, response)
 | 
						|
          : get(response, responsePath);
 | 
						|
        set(response, responsePath, null);
 | 
						|
        this.storeDataset(modelName, query, response, dataset);
 | 
						|
        return this.fetchPage(modelName, query);
 | 
						|
      })
 | 
						|
      .catch(function (e) {
 | 
						|
        throw e;
 | 
						|
      });
 | 
						|
  }
 | 
						|
 | 
						|
  filterData(filter, dataset) {
 | 
						|
    let newData = dataset || [];
 | 
						|
    if (filter) {
 | 
						|
      if (filter instanceof Function) {
 | 
						|
        newData = filter(dataset);
 | 
						|
      } else {
 | 
						|
        newData = dataset.filter((item) => {
 | 
						|
          const id = item.id || item.name || item;
 | 
						|
          return id.toLowerCase().includes(filter.toLowerCase());
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return newData;
 | 
						|
  }
 | 
						|
 | 
						|
  // reconstructs the original form of the response from the server
 | 
						|
  // with an additional `meta` block
 | 
						|
  //
 | 
						|
  // the meta block includes:
 | 
						|
  // currentPage, lastPage, nextPage, prevPage, total, filteredTotal
 | 
						|
  constructResponse(modelName, query) {
 | 
						|
    const { pageFilter, responsePath, size, page } = query;
 | 
						|
    const { response, dataset } = this.getDataset(modelName, query);
 | 
						|
    const resp = { ...response };
 | 
						|
    const data = this.filterData(pageFilter, dataset);
 | 
						|
 | 
						|
    const lastPage = Math.ceil(data.length / size);
 | 
						|
    const currentPage = clamp(page, 1, lastPage);
 | 
						|
    const end = currentPage * size;
 | 
						|
    const start = end - size;
 | 
						|
    const slicedDataSet = data.slice(start, end);
 | 
						|
 | 
						|
    set(resp, responsePath || '', slicedDataSet);
 | 
						|
    resp.meta = {
 | 
						|
      currentPage,
 | 
						|
      lastPage,
 | 
						|
      nextPage: clamp(currentPage + 1, 1, lastPage),
 | 
						|
      prevPage: clamp(currentPage - 1, 1, lastPage),
 | 
						|
      total: dataset.length || 0,
 | 
						|
      filteredTotal: data.length || 0,
 | 
						|
      pageSize: size,
 | 
						|
    };
 | 
						|
 | 
						|
    return resp;
 | 
						|
  }
 | 
						|
 | 
						|
  forceUnload(modelName) {
 | 
						|
    // Hack to get unloadAll to work correctly until we update to ember-data@4.12
 | 
						|
    // so that all the records are properly unloaded and we don't get ghost records
 | 
						|
    this.peekAll(modelName).length;
 | 
						|
    // force destroy queue to flush https://github.com/emberjs/data/issues/5447
 | 
						|
    run(() => this.unloadAll(modelName));
 | 
						|
  }
 | 
						|
 | 
						|
  // pushes records into the store and returns the result
 | 
						|
  fetchPage(modelName, query) {
 | 
						|
    const response = this.constructResponse(modelName, query);
 | 
						|
    this.forceUnload(modelName);
 | 
						|
    // Hack to ensure the pushed records below all get in the store. remove with update to ember-data@4.12
 | 
						|
    this.peekAll(modelName).length;
 | 
						|
    return new Promise((resolve) => {
 | 
						|
      // push subset of records into the store
 | 
						|
      schedule('destroy', () => {
 | 
						|
        this.push(
 | 
						|
          this.serializerFor(modelName).normalizeResponse(
 | 
						|
            this,
 | 
						|
            this.modelFor(modelName),
 | 
						|
            response,
 | 
						|
            null,
 | 
						|
            'query'
 | 
						|
          )
 | 
						|
        );
 | 
						|
        // Hack to make sure all records get in model correctly. remove with update to ember-data@4.12
 | 
						|
        this.peekAll(modelName).length;
 | 
						|
        const model = this.peekAll(modelName).toArray();
 | 
						|
        model.set('meta', response.meta);
 | 
						|
        resolve(model);
 | 
						|
      });
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  // get cached data
 | 
						|
  getDataset(modelName, query) {
 | 
						|
    return this.getLazyCacheForModel(modelName, query);
 | 
						|
  }
 | 
						|
 | 
						|
  // store data cache as { response, dataset}
 | 
						|
  // also populated `lazyCaches` attribute
 | 
						|
  storeDataset(modelName, query, response, array) {
 | 
						|
    const dataset = query.sortBy ? sortObjects(array, query.sortBy) : array;
 | 
						|
    const value = {
 | 
						|
      response,
 | 
						|
      dataset,
 | 
						|
    };
 | 
						|
    this.setLazyCacheForModel(modelName, query, value);
 | 
						|
  }
 | 
						|
 | 
						|
  clearDataset(modelName) {
 | 
						|
    if (!this.lazyCaches.size) return;
 | 
						|
    if (modelName && this.lazyCaches.has(modelName)) {
 | 
						|
      this.lazyCaches.delete(modelName);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    this.lazyCaches.clear();
 | 
						|
  }
 | 
						|
 | 
						|
  clearAllDatasets() {
 | 
						|
    this.clearDataset();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * this is designed to be a temporary workaround to an issue in the test environment after upgrading to Ember 4.12
 | 
						|
   * when performing an unloadAll or unloadRecord for auth-method or secret-engine models within the app code an error breaks the tests
 | 
						|
   * after the test run is finished during teardown an unloadAll happens and the error "Expected a stable identifier" is thrown
 | 
						|
   * it seems that when the unload happens in the app, for some reason the mount-config relationship models are not unloaded
 | 
						|
   * then when the unloadAll happens a second time during test teardown there seems to be an issue since those records should already have been unloaded
 | 
						|
   * when logging in the teardownRecord hook, it appears that other embedded inverse: null relationships such as replication-attributes are torn down when the parent model is unloaded
 | 
						|
   * the following fixes the issue by explicitly unloading the mount-config models associated to the parent
 | 
						|
   * this should be looked into further to find the root cause, at which time these overrides may be removed
 | 
						|
   */
 | 
						|
  unloadAll(modelName) {
 | 
						|
    const hasMountConfig = ['auth-method', 'secret-engine'];
 | 
						|
    if (hasMountConfig.includes(modelName)) {
 | 
						|
      this.peekAll(modelName).forEach((record) => this.unloadRecord(record));
 | 
						|
    } else {
 | 
						|
      super.unloadAll(modelName);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  unloadRecord(record) {
 | 
						|
    const hasMountConfig = ['auth-method', 'secret-engine'];
 | 
						|
    if (record && hasMountConfig.includes(record.constructor.modelName) && record.config) {
 | 
						|
      super.unloadRecord(record.config);
 | 
						|
    }
 | 
						|
    super.unloadRecord(record);
 | 
						|
  }
 | 
						|
}
 |