123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import {Component} from 'react';
- import styled from '@emotion/styled';
- import capitalize from 'lodash/capitalize';
- import pick from 'lodash/pick';
- import {t, tct} from 'app/locale';
- import {ExternalActorMapping, Integration, Organization} from 'app/types';
- import {FieldFromConfig} from 'app/views/settings/components/forms';
- import Form from 'app/views/settings/components/forms/form';
- import {Field} from 'app/views/settings/components/forms/type';
- type Props = Pick<Form['props'], 'onSubmitSuccess' | 'onCancel'> &
- Partial<Pick<Form['props'], 'onSubmit'>> & {
- organization: Organization;
- integration: Integration;
- mapping?: ExternalActorMapping;
- type: 'user' | 'team';
- baseEndpoint?: string;
- sentryNamesMapper: (v: any) => {id: string; name: string}[];
- url: string;
- onResults?: (data: any) => void;
- };
- export default class IntegrationExternalMappingForm extends Component<Props> {
- get initialData() {
- const {integration, mapping} = this.props;
- return {
- externalName: '',
- userId: '',
- teamId: '',
- sentryName: '',
- provider: integration.provider.key,
- integrationId: integration.id,
- ...pick(mapping, ['externalName', 'userId', 'sentryName', 'teamId']),
- };
- }
- get formFields(): Field[] {
- const {type, sentryNamesMapper, url, mapping} = this.props;
- const optionMapper = sentryNames =>
- sentryNames.map(({name, id}) => ({value: id, label: name}));
- const fields: any[] = [
- {
- name: 'externalName',
- type: 'string',
- required: true,
- label: tct('External [type]', {type: capitalize(type)}),
- placeholder: t(`${type === 'team' ? '@org/teamname' : '@username'}`),
- },
- ];
- if (type === 'user') {
- fields.push({
- name: 'userId',
- type: 'select_async',
- required: true,
- label: tct('Sentry [type]', {type: capitalize(type)}),
- placeholder: t(`Choose your Sentry User`),
- url,
- onResults: result => {
- // For organizations with >100 users, we want to make sure their
- // saved mapping gets populated in the results if it wouldn't have
- // been in the initial 100 API results, which is why we add it here
- if (mapping && !result.find(({user}) => user.id === mapping.userId)) {
- result = [{id: mapping.userId, name: mapping.sentryName}, ...result];
- }
- this.props.onResults?.(result);
- return optionMapper(sentryNamesMapper(result));
- },
- });
- }
- if (type === 'team') {
- fields.push({
- name: 'teamId',
- type: 'select_async',
- required: true,
- label: tct('Sentry [type]', {type: capitalize(type)}),
- placeholder: t(`Choose your Sentry Team`),
- url,
- onResults: result => {
- // For organizations with >100 teams, we want to make sure their
- // saved mapping gets populated in the results if it wouldn't have
- // been in the initial 100 API results, which is why we add it here
- if (mapping && !result.find(({id}) => id === mapping.teamId)) {
- result = [{id: mapping.teamId, name: mapping.sentryName}, ...result];
- }
- // The team needs `this.props.onResults` so that we have team slug
- // when a user submits a team mapping, the endpoint needs the slug
- // as a path param: /teams/${organization.slug}/${team.slug}/external-teams/
- this.props.onResults?.(result);
- return optionMapper(sentryNamesMapper(result));
- },
- });
- }
- return fields;
- }
- render() {
- const {onSubmitSuccess, onCancel, mapping, baseEndpoint, onSubmit} = this.props;
- // endpoint changes if we are making a new row or updating an existing one
- const endpoint = !baseEndpoint
- ? undefined
- : mapping
- ? `${baseEndpoint}${mapping.id}/`
- : baseEndpoint;
- const apiMethod = !baseEndpoint ? undefined : mapping ? 'PUT' : 'POST';
- return (
- <FormWrapper>
- <Form
- requireChanges
- onSubmitSuccess={onSubmitSuccess}
- initialData={this.initialData}
- apiEndpoint={endpoint}
- apiMethod={apiMethod}
- onCancel={onCancel}
- onSubmit={onSubmit}
- >
- {this.formFields.map(field => (
- <FieldFromConfig
- key={field.name}
- field={field}
- inline={false}
- stacked
- flexibleControlStateSize
- />
- ))}
- </Form>
- </FormWrapper>
- );
- }
- }
- // Prevents errors from appearing off the modal
- const FormWrapper = styled('div')`
- position: relative;
- `;
|