Skip to content

Commit

Permalink
Merge branch 'main' into migrate-modules-12
Browse files Browse the repository at this point in the history
  • Loading branch information
eddeee888 committed Jul 13, 2024
2 parents 3ef57d6 + e8daf5c commit 12cd663
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 40 deletions.
141 changes: 102 additions & 39 deletions packages/services/api/src/modules/support/providers/support-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,56 +195,114 @@ export class SupportManager {
organizationId: string;
}): Promise<string> {
const organizationZendeskId = await this.ensureZendeskOrganizationId(input.organizationId);

const userAsMember = await this.organizationManager.getOrganizationMember({
organization: input.organizationId,
user: input.userId,
});

if (!userAsMember.user.zendeskId) {
this.logger.info(
'Creating user in zendesk organization (organization: %s, user: %s)',
'Attempt to find user via Zendesk API. (organizationID: %s, userId: %s)',
input.organizationId,
input.userId,
);
const response = await this.httpClient
.post(`https://${this.config.subdomain}.zendesk.com/api/v2/users`, {

const email = userAsMember.user.email;

// Before attempting to create the user we need to check whether an user with that email might already exist.
let userZendeskId = await this.httpClient
.get(`https://${this.config.subdomain}.zendesk.com/api/v2/users`, {
searchParams: {
query: email,
},
username: this.config.username,
password: this.config.password,
responseType: 'json',
context: {
logger: this.logger,
},
headers: {
'idempotency-key': input.userId,
},
json: {
user: {
name: userAsMember.user.fullName,
email: userAsMember.user.email,
external_id: userAsMember.user.id,
identities: [
{
type: 'foreign',
value: userAsMember.user.email,
},
],
role: 'end-user',
verified: true,
},
skip_verify_email: true,
},
})
.then(res =>
UserCreateResponseModel.parseAsync(res).catch(err => {
this.logger.error(err);
return Promise.reject(err);
}),
.then(res => {
const data = z
.object({
users: z.array(
z.object({
id: z.number(),
email: z.string(),
organization_id: z.number().nullable(),
}),
),
})
.parse(res);

const user = data.users.at(0) ?? null;

if (user?.email === email) {
this.logger.info(
'User found on Zendesk. (organizationID: %s, userId: %s)',
input.organizationId,
input.userId,
);
return user.id;
}

this.logger.info(
'User not found on Zendesk. (organizationID: %s, userId: %s)',
input.organizationId,
input.userId,
);
return null;
});

if (userZendeskId === null) {
this.logger.info(
'Creating user in zendesk organization (organization: %s, user: %s)',
input.organizationId,
input.userId,
);
const userZendeskId = String(response.user.id);
await this.storage.setZendeskUserId({ userId: input.userId, zendeskId: userZendeskId });

userAsMember.user.zendeskId = userZendeskId;
const response = await this.httpClient
.post(`https://${this.config.subdomain}.zendesk.com/api/v2/users`, {
username: this.config.username,
password: this.config.password,
responseType: 'json',
context: {
logger: this.logger,
},
headers: {
'idempotency-key': input.userId,
},
json: {
user: {
name: userAsMember.user.fullName,
email,
external_id: userAsMember.user.id,
identities: [
{
type: 'foreign',
value: email,
},
],
role: 'end-user',
verified: true,
},
skip_verify_email: true,
},
})
.then(res =>
UserCreateResponseModel.parseAsync(res).catch(err => {
this.logger.error(err);
return Promise.reject(err);
}),
);
userZendeskId = response.user.id;
}

await this.storage.setZendeskUserId({
userId: input.userId,
zendeskId: String(userZendeskId),
});
userAsMember.user.zendeskId = String(userZendeskId);
}

if (!userAsMember.connectedToZendesk) {
Expand All @@ -264,12 +322,13 @@ export class SupportManager {
logger: this.logger,
},
headers: {
'idempotency-key': input.userId + ':' + input.organizationId,
// v2 post fix is for idemopotency key cache busting.
'idempotency-key': input.userId + '|v2',
},
json: {
organization_membership: {
user_id: userAsMember.user.zendeskId,
organization_id: organizationZendeskId,
user_id: parseInt(userAsMember.user.zendeskId, 10),
organization_id: parseInt(organizationZendeskId, 10),
},
},
},
Expand Down Expand Up @@ -441,8 +500,8 @@ export class SupportManager {
this.logger.info(
'Creating support ticket (organization: %s, priority: %s, subject: %s)',
input.organizationId,
input.subject,
input.priority,
input.subject,
);

const request = SupportTicketCreateRequestModel.safeParse(input);
Expand Down Expand Up @@ -475,6 +534,8 @@ export class SupportManager {
subject: input.subject,
description: input.description,
priority: input.priority,
// version is here to cache bust the idempotency key.
version: 'v2',
}),
)
.digest('hex');
Expand All @@ -490,9 +551,9 @@ export class SupportManager {
password: this.config.password,
json: {
ticket: {
organization_id: internalOrganizationId,
submitter_id: internalUserId,
requester_id: internalUserId,
organization_id: parseInt(internalOrganizationId, 10),
submitter_id: parseInt(internalUserId, 10),
requester_id: parseInt(internalUserId, 10),
comment: {
body: request.data.description,
},
Expand Down Expand Up @@ -559,6 +620,8 @@ export class SupportManager {
ticketId: input.ticketId,
body: input.body,
internalUserId,
// increment for cache busting.
version: '2',
}),
)
.digest('hex');
Expand All @@ -582,7 +645,7 @@ export class SupportManager {
ticket: {
comment: {
body: request.data.body,
author_id: internalUserId,
author_id: parseInt(internalUserId, 10),
public: true,
},
},
Expand Down
10 changes: 9 additions & 1 deletion packages/web/app/src/pages/target-history-version.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { DiffEditor, Spinner } from '@/components/v2';
import { DiffIcon } from '@/components/v2/icon';
import { FragmentType, graphql, useFragment } from '@/gql';
import { CriticalityLevel, ProjectType } from '@/gql/graphql';
import { cn } from '@/lib/utils';
import {
CheckCircledIcon,
CrossCircledIcon,
Expand Down Expand Up @@ -168,6 +169,7 @@ function SchemaVersionView(props: {
targetId={props.targetId}
schemaVersion={schemaVersion}
projectType={props.projectType}
hasContracts={!!schemaVersion.contractVersions?.edges}
/>
)}
</div>
Expand Down Expand Up @@ -222,6 +224,7 @@ function DefaultSchemaVersionView(props: {
organizationId: string;
projectId: string;
targetId: string;
hasContracts: boolean;
}) {
const schemaVersion = useFragment(
DefaultSchemaVersionView_SchemaVersionFragment,
Expand Down Expand Up @@ -280,7 +283,12 @@ function DefaultSchemaVersionView(props: {
<>
<TooltipProvider>
<Tabs value={selectedView} onValueChange={value => setSelectedView(value)}>
<TabsList className="bg-background border-muted w-full justify-start rounded-none border-x border-b">
<TabsList
className={cn(
'bg-background border-muted w-full justify-start rounded-none border-x border-b',
!props.hasContracts && 'rounded-t border-t',
)}
>
{availableViews.map(item => (
<Tooltip key={item.value}>
<TooltipTrigger>
Expand Down

0 comments on commit 12cd663

Please sign in to comment.