lodash#trimEnd TypeScript Examples

The following examples show how to use lodash#trimEnd. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: BitbucketCloudUrlReader.ts    From backstage with Apache License 2.0 6 votes vote down vote up
async search(url: string, options?: SearchOptions): Promise<SearchResponse> {
    const { filepath } = parseGitUrl(url);
    const matcher = new Minimatch(filepath);

    // TODO(freben): For now, read the entire repo and filter through that. In
    // a future improvement, we could be smart and try to deduce that non-glob
    // prefixes (like for filepaths such as some-prefix/**/a.yaml) can be used
    // to get just that part of the repo.
    const treeUrl = trimEnd(url.replace(filepath, ''), '/');

    const tree = await this.readTree(treeUrl, {
      etag: options?.etag,
      filter: path => matcher.match(path),
    });
    const files = await tree.files();

    return {
      etag: tree.etag,
      files: files.map(file => ({
        url: this.integration.resolveUrl({
          url: `/${file.path}`,
          base: url,
        }),
        content: file.content,
      })),
    };
  }
Example #2
Source File: BitbucketServerUrlReader.ts    From backstage with Apache License 2.0 6 votes vote down vote up
async search(url: string, options?: SearchOptions): Promise<SearchResponse> {
    const { filepath } = parseGitUrl(url);
    const matcher = new Minimatch(filepath);

    // TODO(freben): For now, read the entire repo and filter through that. In
    // a future improvement, we could be smart and try to deduce that non-glob
    // prefixes (like for filepaths such as some-prefix/**/a.yaml) can be used
    // to get just that part of the repo.
    const treeUrl = trimEnd(url.replace(filepath, ''), '/');

    const tree = await this.readTree(treeUrl, {
      etag: options?.etag,
      filter: path => matcher.match(path),
    });
    const files = await tree.files();

    return {
      etag: tree.etag,
      files: files.map(file => ({
        url: this.integration.resolveUrl({
          url: `/${file.path}`,
          base: url,
        }),
        content: file.content,
      })),
    };
  }
Example #3
Source File: BitbucketUrlReader.ts    From backstage with Apache License 2.0 6 votes vote down vote up
async search(url: string, options?: SearchOptions): Promise<SearchResponse> {
    const { filepath } = parseGitUrl(url);
    const matcher = new Minimatch(filepath);

    // TODO(freben): For now, read the entire repo and filter through that. In
    // a future improvement, we could be smart and try to deduce that non-glob
    // prefixes (like for filepaths such as some-prefix/**/a.yaml) can be used
    // to get just that part of the repo.
    const treeUrl = trimEnd(url.replace(filepath, ''), '/');

    const tree = await this.readTree(treeUrl, {
      etag: options?.etag,
      filter: path => matcher.match(path),
    });
    const files = await tree.files();

    return {
      etag: tree.etag,
      files: files.map(file => ({
        url: this.integration.resolveUrl({
          url: `/${file.path}`,
          base: url,
        }),
        content: file.content,
      })),
    };
  }
Example #4
Source File: GitlabUrlReader.ts    From backstage with Apache License 2.0 6 votes vote down vote up
async search(url: string, options?: SearchOptions): Promise<SearchResponse> {
    const { filepath } = parseGitUrl(url);
    const matcher = new Minimatch(filepath);

    // TODO(freben): For now, read the entire repo and filter through that. In
    // a future improvement, we could be smart and try to deduce that non-glob
    // prefixes (like for filepaths such as some-prefix/**/a.yaml) can be used
    // to get just that part of the repo.
    const treeUrl = trimEnd(url.replace(filepath, ''), '/');

    const tree = await this.readTree(treeUrl, {
      etag: options?.etag,
      signal: options?.signal,
      filter: path => matcher.match(stripFirstDirectoryFromPath(path)),
    });
    const files = await tree.files();

    return {
      etag: tree.etag,
      files: files.map(file => ({
        url: this.integration.resolveUrl({ url: `/${file.path}`, base: url }),
        content: file.content,
      })),
    };
  }
Example #5
Source File: config.ts    From backstage with Apache License 2.0 6 votes vote down vote up
/**
 * Reads a single Bitbucket integration config.
 *
 * @param config - The config object of a single integration
 * @public
 * @deprecated bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer.
 */
export function readBitbucketIntegrationConfig(
  config: Config,
): BitbucketIntegrationConfig {
  const host = config.getOptionalString('host') ?? BITBUCKET_HOST;
  let apiBaseUrl = config.getOptionalString('apiBaseUrl');
  const token = config.getOptionalString('token');
  const username = config.getOptionalString('username');
  const appPassword = config.getOptionalString('appPassword');

  if (!isValidHost(host)) {
    throw new Error(
      `Invalid Bitbucket integration config, '${host}' is not a valid host`,
    );
  }

  if (apiBaseUrl) {
    apiBaseUrl = trimEnd(apiBaseUrl, '/');
  } else if (host === BITBUCKET_HOST) {
    apiBaseUrl = BITBUCKET_API_BASE_URL;
  } else {
    apiBaseUrl = `https://${host}/rest/api/1.0`;
  }

  return {
    host,
    apiBaseUrl,
    token,
    username,
    appPassword,
  };
}
Example #6
Source File: config.ts    From backstage with Apache License 2.0 6 votes vote down vote up
/**
 * Reads a single Bitbucket Server integration config.
 *
 * @param config - The config object of a single integration
 * @public
 */
export function readBitbucketServerIntegrationConfig(
  config: Config,
): BitbucketServerIntegrationConfig {
  const host = config.getString('host');
  let apiBaseUrl = config.getOptionalString('apiBaseUrl');
  const token = config.getOptionalString('token');

  if (!isValidHost(host)) {
    throw new Error(
      `Invalid Bitbucket Server integration config, '${host}' is not a valid host`,
    );
  }

  if (apiBaseUrl) {
    apiBaseUrl = trimEnd(apiBaseUrl, '/');
  } else {
    apiBaseUrl = `https://${host}/rest/api/1.0`;
  }

  return {
    host,
    apiBaseUrl,
    token,
  };
}
Example #7
Source File: PreviewCatalogInfoComponent.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
PreviewCatalogInfoComponent = (
  props: PreviewCatalogInfoComponentProps,
) => {
  const { repositoryUrl, entities, classes } = props;
  const catalogFilename = useCatalogFilename();

  return (
    <Card variant="outlined" className={classes?.card}>
      <CardHeader
        title={
          <code>{`${trimEnd(repositoryUrl, '/')}/${catalogFilename}`}</code>
        }
      />

      <CardContent className={classes?.cardContent}>
        <CodeSnippet
          text={entities
            .map(e => YAML.stringify(e))
            .join('---\n')
            .trim()}
          language="yaml"
        />
      </CardContent>
    </Card>
  );
}
Example #8
Source File: config.ts    From backstage with Apache License 2.0 5 votes vote down vote up
/**
 * Reads a single Gerrit integration config.
 *
 * @param config - The config object of a single integration
 *
 * @public
 */
export function readGerritIntegrationConfig(
  config: Config,
): GerritIntegrationConfig {
  const host = config.getString('host');
  let baseUrl = config.getOptionalString('baseUrl');
  let cloneUrl = config.getOptionalString('cloneUrl');
  let gitilesBaseUrl = config.getOptionalString('gitilesBaseUrl');
  const username = config.getOptionalString('username');
  const password = config.getOptionalString('password');

  if (!isValidHost(host)) {
    throw new Error(
      `Invalid Gerrit integration config, '${host}' is not a valid host`,
    );
  } else if (baseUrl && !isValidUrl(baseUrl)) {
    throw new Error(
      `Invalid Gerrit integration config, '${baseUrl}' is not a valid baseUrl`,
    );
  } else if (cloneUrl && !isValidUrl(cloneUrl)) {
    throw new Error(
      `Invalid Gerrit integration config, '${cloneUrl}' is not a valid cloneUrl`,
    );
  } else if (gitilesBaseUrl && !isValidUrl(gitilesBaseUrl)) {
    throw new Error(
      `Invalid Gerrit integration config, '${gitilesBaseUrl}' is not a valid gitilesBaseUrl`,
    );
  }
  if (baseUrl) {
    baseUrl = trimEnd(baseUrl, '/');
  } else {
    baseUrl = `https://${host}`;
  }
  if (gitilesBaseUrl) {
    gitilesBaseUrl = trimEnd(gitilesBaseUrl, '/');
  } else {
    gitilesBaseUrl = `https://${host}`;
  }
  if (cloneUrl) {
    cloneUrl = trimEnd(cloneUrl, '/');
  } else {
    cloneUrl = baseUrl;
  }

  return {
    host,
    baseUrl,
    cloneUrl,
    gitilesBaseUrl,
    username,
    password,
  };
}
Example #9
Source File: config.ts    From backstage with Apache License 2.0 5 votes vote down vote up
/**
 * Reads a single GitHub integration config.
 *
 * @param config - The config object of a single integration
 * @public
 */
export function readGitHubIntegrationConfig(
  config: Config,
): GitHubIntegrationConfig {
  const host = config.getOptionalString('host') ?? GITHUB_HOST;
  let apiBaseUrl = config.getOptionalString('apiBaseUrl');
  let rawBaseUrl = config.getOptionalString('rawBaseUrl');
  const token = config.getOptionalString('token');
  const apps = config.getOptionalConfigArray('apps')?.map(c => ({
    appId: c.getNumber('appId'),
    clientId: c.getString('clientId'),
    clientSecret: c.getString('clientSecret'),
    webhookSecret: c.getString('webhookSecret'),
    privateKey: c.getString('privateKey'),
    allowedInstallationOwners: c.getOptionalStringArray(
      'allowedInstallationOwners',
    ),
  }));

  if (!isValidHost(host)) {
    throw new Error(
      `Invalid GitHub integration config, '${host}' is not a valid host`,
    );
  }

  if (apiBaseUrl) {
    apiBaseUrl = trimEnd(apiBaseUrl, '/');
  } else if (host === GITHUB_HOST) {
    apiBaseUrl = GITHUB_API_BASE_URL;
  }

  if (rawBaseUrl) {
    rawBaseUrl = trimEnd(rawBaseUrl, '/');
  } else if (host === GITHUB_HOST) {
    rawBaseUrl = GITHUB_RAW_BASE_URL;
  }

  return { host, apiBaseUrl, rawBaseUrl, token, apps };
}
Example #10
Source File: config.ts    From backstage with Apache License 2.0 5 votes vote down vote up
/**
 * Reads a single GitLab integration config.
 *
 * @param config - The config object of a single integration
 * @public
 */
export function readGitLabIntegrationConfig(
  config: Config,
): GitLabIntegrationConfig {
  const host = config.getString('host');
  let apiBaseUrl = config.getOptionalString('apiBaseUrl');
  const token = config.getOptionalString('token');
  let baseUrl = config.getOptionalString('baseUrl');

  if (apiBaseUrl) {
    apiBaseUrl = trimEnd(apiBaseUrl, '/');
  } else if (host === GITLAB_HOST) {
    apiBaseUrl = GITLAB_API_BASE_URL;
  }

  if (baseUrl) {
    baseUrl = trimEnd(baseUrl, '/');
  } else {
    baseUrl = `https://${host}`;
  }

  if (!isValidHost(host)) {
    throw new Error(
      `Invalid GitLab integration config, '${host}' is not a valid host`,
    );
  } else if (!apiBaseUrl || !isValidUrl(apiBaseUrl)) {
    throw new Error(
      `Invalid GitLab integration config, '${apiBaseUrl}' is not a valid apiBaseUrl`,
    );
  } else if (!isValidUrl(baseUrl)) {
    throw new Error(
      `Invalid GitLab integration config, '${baseUrl}' is not a valid baseUrl`,
    );
  }

  return { host, token, apiBaseUrl, baseUrl };
}
Example #11
Source File: helpers.ts    From backstage with Apache License 2.0 5 votes vote down vote up
/**
 * Default implementation of {@link ScmIntegration} `resolveUrl`, that only
 * works with URL pathname based providers.
 *
 * @public
 */
export function defaultScmResolveUrl(options: {
  url: string;
  base: string;
  lineNumber?: number;
}): string {
  const { url, base, lineNumber } = options;

  // If it is a fully qualified URL - then return it verbatim
  try {
    // eslint-disable-next-line no-new
    new URL(url);
    return url;
  } catch {
    // ignore intentionally
  }

  let updated: URL;

  if (url.startsWith('/')) {
    // If it is an absolute path, move relative to the repo root
    const { filepath } = parseGitUrl(base);
    updated = new URL(base);
    const repoRootPath = trimEnd(
      updated.pathname.substring(0, updated.pathname.length - filepath.length),
      '/',
    );
    updated.pathname = `${repoRootPath}${url}`;
  } else {
    // For relative URLs, just let the default URL constructor handle the
    // resolving. Note that this essentially will treat the last segment of the
    // base as a file - NOT a folder - unless the url ends in a slash.
    updated = new URL(url, base);
  }

  updated.search = new URL(base).search;
  if (lineNumber) {
    updated.hash = `L${lineNumber}`;
  }
  return updated.toString();
}
Example #12
Source File: config.ts    From backstage with Apache License 2.0 5 votes vote down vote up
/**
 * Parses configuration.
 *
 * @param config - The root of the msgraph config hierarchy
 *
 * @public
 */
export function readMicrosoftGraphConfig(
  config: Config,
): MicrosoftGraphProviderConfig[] {
  const providers: MicrosoftGraphProviderConfig[] = [];
  const providerConfigs = config.getOptionalConfigArray('providers') ?? [];

  for (const providerConfig of providerConfigs) {
    const target = trimEnd(providerConfig.getString('target'), '/');

    const authority = providerConfig.getOptionalString('authority')
      ? trimEnd(providerConfig.getOptionalString('authority'), '/')
      : 'https://login.microsoftonline.com';
    const tenantId = providerConfig.getString('tenantId');
    const clientId = providerConfig.getString('clientId');
    const clientSecret = providerConfig.getString('clientSecret');

    const userExpand = providerConfig.getOptionalString('userExpand');
    const userFilter = providerConfig.getOptionalString('userFilter');
    const userGroupMemberFilter = providerConfig.getOptionalString(
      'userGroupMemberFilter',
    );
    const userGroupMemberSearch = providerConfig.getOptionalString(
      'userGroupMemberSearch',
    );
    const groupExpand = providerConfig.getOptionalString('groupExpand');
    const groupFilter = providerConfig.getOptionalString('groupFilter');
    const groupSearch = providerConfig.getOptionalString('groupSearch');

    if (userFilter && userGroupMemberFilter) {
      throw new Error(
        `userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`,
      );
    }
    if (userFilter && userGroupMemberSearch) {
      throw new Error(
        `userGroupMemberSearch cannot be specified when userFilter is defined.`,
      );
    }

    const groupSelect = providerConfig.getOptionalStringArray('groupSelect');
    const queryMode = providerConfig.getOptionalString('queryMode');
    if (
      queryMode !== undefined &&
      queryMode !== 'basic' &&
      queryMode !== 'advanced'
    ) {
      throw new Error(`queryMode must be one of: basic, advanced`);
    }

    providers.push({
      target,
      authority,
      tenantId,
      clientId,
      clientSecret,
      userExpand,
      userFilter,
      userGroupMemberFilter,
      userGroupMemberSearch,
      groupExpand,
      groupFilter,
      groupSearch,
      groupSelect,
      queryMode,
    });
  }

  return providers;
}
Example #13
Source File: CatalogImportClient.ts    From backstage with Apache License 2.0 5 votes vote down vote up
// TODO: this response should better be part of the analyze-locations response and scm-independent / implemented per scm
  private async checkGitHubForExistingCatalogInfo(options: {
    url: string;
    owner: string;
    repo: string;
    githubIntegrationConfig: GitHubIntegrationConfig;
  }): Promise<
    Array<{
      target: string;
      entities: CompoundEntityRef[];
    }>
  > {
    const { url, owner, repo, githubIntegrationConfig } = options;

    const { token } = await this.scmAuthApi.getCredentials({ url });
    const octo = new Octokit({
      auth: token,
      baseUrl: githubIntegrationConfig.apiBaseUrl,
    });
    const catalogFilename = getCatalogFilename(this.configApi);
    const query = `repo:${owner}/${repo}+filename:${catalogFilename}`;

    const searchResult = await octo.search.code({ q: query }).catch(e => {
      throw new Error(
        formatHttpErrorMessage(
          "Couldn't search repository for metadata file.",
          e,
        ),
      );
    });
    const exists = searchResult.data.total_count > 0;
    if (exists) {
      const repoInformation = await octo.repos.get({ owner, repo }).catch(e => {
        throw new Error(formatHttpErrorMessage("Couldn't fetch repo data", e));
      });
      const defaultBranch = repoInformation.data.default_branch;

      return await Promise.all(
        searchResult.data.items
          .map(i => `${trimEnd(url, '/')}/blob/${defaultBranch}/${i.path}`)
          .map(async target => {
            const result = await this.catalogApi.addLocation({
              type: 'url',
              target,
              dryRun: true,
            });
            return {
              target,
              exists: result.exists,
              entities: result.entities.map(e => ({
                kind: e.kind,
                namespace: e.metadata.namespace ?? 'default',
                name: e.metadata.name,
              })),
            };
          }),
      );
    }

    return [];
  }
Example #14
Source File: config.ts    From backstage with Apache License 2.0 4 votes vote down vote up
/**
 * Parses configuration.
 *
 * @param config - The root of the LDAP config hierarchy
 *
 * @public
 */
export function readLdapConfig(config: Config): LdapProviderConfig[] {
  function freeze<T>(data: T): T {
    return JSON.parse(JSON.stringify(data), (_key, value) => {
      if (typeof value === 'object' && value !== null) {
        Object.freeze(value);
      }
      return value;
    });
  }

  function readTlsConfig(
    c: Config | undefined,
  ): LdapProviderConfig['tls'] | undefined {
    if (!c) {
      return undefined;
    }
    return {
      rejectUnauthorized: c.getOptionalBoolean('rejectUnauthorized'),
    };
  }

  function readBindConfig(
    c: Config | undefined,
  ): LdapProviderConfig['bind'] | undefined {
    if (!c) {
      return undefined;
    }
    return {
      dn: c.getString('dn'),
      secret: c.getString('secret'),
    };
  }

  function readOptionsConfig(c: Config | undefined): SearchOptions {
    if (!c) {
      return {};
    }

    const paged = readOptionsPagedConfig(c);

    return {
      scope: c.getOptionalString('scope') as SearchOptions['scope'],
      filter: formatFilter(c.getOptionalString('filter')),
      attributes: c.getOptionalStringArray('attributes'),
      sizeLimit: c.getOptionalNumber('sizeLimit'),
      timeLimit: c.getOptionalNumber('timeLimit'),
      derefAliases: c.getOptionalNumber('derefAliases'),
      typesOnly: c.getOptionalBoolean('typesOnly'),
      ...(paged !== undefined ? { paged } : undefined),
    };
  }

  function readOptionsPagedConfig(c: Config): SearchOptions['paged'] {
    const pagedConfig = c.getOptional('paged');
    if (pagedConfig === undefined) {
      return undefined;
    }

    if (pagedConfig === true || pagedConfig === false) {
      return pagedConfig;
    }

    const pageSize = c.getOptionalNumber('paged.pageSize');
    const pagePause = c.getOptionalBoolean('paged.pagePause');
    return {
      ...(pageSize !== undefined ? { pageSize } : undefined),
      ...(pagePause !== undefined ? { pagePause } : undefined),
    };
  }

  function readSetConfig(
    c: Config | undefined,
  ): { [path: string]: JsonValue } | undefined {
    if (!c) {
      return undefined;
    }
    return c.get();
  }

  function readUserMapConfig(
    c: Config | undefined,
  ): Partial<LdapProviderConfig['users']['map']> {
    if (!c) {
      return {};
    }

    return {
      rdn: c.getOptionalString('rdn'),
      name: c.getOptionalString('name'),
      description: c.getOptionalString('description'),
      displayName: c.getOptionalString('displayName'),
      email: c.getOptionalString('email'),
      picture: c.getOptionalString('picture'),
      memberOf: c.getOptionalString('memberOf'),
    };
  }

  function readGroupMapConfig(
    c: Config | undefined,
  ): Partial<LdapProviderConfig['groups']['map']> {
    if (!c) {
      return {};
    }

    return {
      rdn: c.getOptionalString('rdn'),
      name: c.getOptionalString('name'),
      description: c.getOptionalString('description'),
      type: c.getOptionalString('type'),
      displayName: c.getOptionalString('displayName'),
      email: c.getOptionalString('email'),
      picture: c.getOptionalString('picture'),
      memberOf: c.getOptionalString('memberOf'),
      members: c.getOptionalString('members'),
    };
  }

  function readUserConfig(
    c: Config,
  ): RecursivePartial<LdapProviderConfig['users']> {
    return {
      dn: c.getString('dn'),
      options: readOptionsConfig(c.getOptionalConfig('options')),
      set: readSetConfig(c.getOptionalConfig('set')),
      map: readUserMapConfig(c.getOptionalConfig('map')),
    };
  }

  function readGroupConfig(
    c: Config,
  ): RecursivePartial<LdapProviderConfig['groups']> {
    return {
      dn: c.getString('dn'),
      options: readOptionsConfig(c.getOptionalConfig('options')),
      set: readSetConfig(c.getOptionalConfig('set')),
      map: readGroupMapConfig(c.getOptionalConfig('map')),
    };
  }

  function formatFilter(filter?: string): string | undefined {
    // Remove extra whitespace between blocks to support multiline filters from the configuration
    return filter?.replace(/\s*(\(|\))/g, '$1')?.trim();
  }

  const providerConfigs = config.getOptionalConfigArray('providers') ?? [];
  return providerConfigs.map(c => {
    const newConfig = {
      target: trimEnd(c.getString('target'), '/'),
      tls: readTlsConfig(c.getOptionalConfig('tls')),
      bind: readBindConfig(c.getOptionalConfig('bind')),
      users: readUserConfig(c.getConfig('users')),
      groups: readGroupConfig(c.getConfig('groups')),
    };
    const merged = mergeWith({}, defaultConfig, newConfig, (_into, from) => {
      // Replace arrays instead of merging, otherwise default behavior
      return Array.isArray(from) ? from : undefined;
    });
    return freeze(merged) as LdapProviderConfig;
  });
}