Skip to content

Commit c40ea2c

Browse files
authored
feat(api): refactor Query.project + expose via public API (#6654)
1 parent 012a8bd commit c40ea2c

File tree

17 files changed

+161
-102
lines changed

17 files changed

+161
-102
lines changed

integration-tests/testkit/flow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ export function readProjectInfo(
607607
return execute({
608608
document: graphql(`
609609
query readProjectInfo($selector: ProjectSelectorInput!) {
610-
project(selector: $selector) {
610+
project(reference: { bySelector: $selector }) {
611611
id
612612
slug
613613
}

integration-tests/tests/api/policy/policy-access.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('Policy Access', () => {
88
describe('Project', () => {
99
const query = graphql(`
1010
query ProjectSchemaPolicyAccess($selector: ProjectSelectorInput!) {
11-
project(selector: $selector) {
11+
project(reference: { bySelector: $selector }) {
1212
schemaPolicy {
1313
id
1414
}

packages/services/api/src/modules/project/module.graphql.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { gql } from 'graphql-modules';
22

33
export default gql`
44
extend type Query {
5-
project(selector: ProjectSelectorInput!): Project
5+
project(reference: ProjectReferenceInput! @tag(name: "public")): Project @tag(name: "public")
66
projects(selector: OrganizationSelectorInput!): ProjectConnection!
77
}
88
@@ -66,8 +66,8 @@ export default gql`
6666
}
6767
6868
input ProjectSelectorInput {
69-
organizationSlug: String!
70-
projectSlug: String!
69+
organizationSlug: String! @tag(name: "public")
70+
projectSlug: String! @tag(name: "public")
7171
}
7272
7373
type ProjectSelector {
@@ -87,9 +87,14 @@ export default gql`
8787
projectBySlug(projectSlug: String!): Project
8888
}
8989
90+
input ProjectReferenceInput @oneOf {
91+
byId: ID @tag(name: "public")
92+
bySelector: ProjectSelectorInput @tag(name: "public")
93+
}
94+
9095
type Project {
91-
id: ID!
92-
slug: String!
96+
id: ID! @tag(name: "public")
97+
slug: String! @tag(name: "public")
9398
cleanId: ID! @deprecated(reason: "Use the 'slug' field instead.")
9499
name: String! @deprecated(reason: "Use the 'slug' field instead.")
95100
type: ProjectType!

packages/services/api/src/modules/project/providers/project-manager.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Injectable, Scope } from 'graphql-modules';
2+
import type { ProjectReferenceInput } from '../../../__generated__/types';
23
import type { Organization, Project, ProjectType } from '../../../shared/entities';
34
import { AuditLogRecorder } from '../../audit-logs/providers/audit-log-recorder';
45
import { Session } from '../../auth/lib/authz';
6+
import { IdTranslator } from '../../shared/providers/id-translator';
57
import { Logger } from '../../shared/providers/logger';
68
import { OrganizationSelector, ProjectSelector, Storage } from '../../shared/providers/storage';
79
import { TokenStorage } from '../../token/providers/token-storage';
@@ -25,6 +27,7 @@ export class ProjectManager {
2527
private session: Session,
2628
private tokenStorage: TokenStorage,
2729
private auditLog: AuditLogRecorder,
30+
private idTranslator: IdTranslator,
2831
) {
2932
this.logger = logger.child({ source: 'ProjectManager' });
3033
}
@@ -124,6 +127,16 @@ export class ProjectManager {
124127
return this.storage.getProject(selector);
125128
}
126129

130+
async getProjectByRereference(reference: ProjectReferenceInput): Promise<Project | null> {
131+
const selector = await this.idTranslator.resolveProjectReference({ reference });
132+
133+
if (selector === null) {
134+
this.session.raise('project:describe');
135+
}
136+
137+
return await this.getProject(selector);
138+
}
139+
127140
async getProjectBySlugForOrganization(
128141
organization: Organization,
129142
projectSlug: string,
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
1-
import { IdTranslator } from '../../../shared/providers/id-translator';
21
import { ProjectManager } from '../../providers/project-manager';
32
import type { QueryResolvers } from './../../../../__generated__/types';
43

54
export const project: NonNullable<QueryResolvers['project']> = async (
65
_,
7-
{ selector },
6+
{ reference },
87
{ injector },
98
) => {
10-
const translator = injector.get(IdTranslator);
11-
const [organization, project] = await Promise.all([
12-
translator.translateOrganizationId(selector),
13-
translator.translateProjectId(selector),
14-
]);
15-
return injector.get(ProjectManager).getProject({
16-
projectId: project,
17-
organizationId: organization,
18-
});
9+
return await injector.get(ProjectManager).getProjectByRereference(reference);
1910
};

packages/services/api/src/modules/shared/providers/id-translator.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,64 @@ export class IdTranslator {
218218

219219
return selector;
220220
}
221+
222+
async resolveProjectReference(args: {
223+
reference: GraphQLSchema.ProjectReferenceInput;
224+
}): Promise<{ organizationId: string; projectId: string } | null> {
225+
this.logger.debug('Resolve project reference. (reference=%o)', args.reference);
226+
227+
let selector: {
228+
organizationId: string;
229+
projectId: string;
230+
};
231+
232+
if (args.reference?.bySelector) {
233+
try {
234+
const [organizationId, projectId] = await Promise.all([
235+
this.translateOrganizationId(args.reference.bySelector),
236+
this.translateProjectId(args.reference.bySelector),
237+
]);
238+
this.logger.debug(
239+
'Project selector resolved. (organization=%s, project=%s)',
240+
organizationId,
241+
projectId,
242+
);
243+
244+
selector = {
245+
organizationId,
246+
projectId,
247+
};
248+
} catch (error: unknown) {
249+
this.logger.debug(String(error));
250+
this.logger.debug('Failed to resolve input slug to ids (slug=%o)', args.reference);
251+
return null;
252+
}
253+
} else {
254+
if (!isUUID(args.reference.byId)) {
255+
this.logger.debug('Invalid uuid provided. (targetId=%s)', args.reference.byId);
256+
return null;
257+
}
258+
259+
const project = await this.storage.getProjectById(args.reference.byId);
260+
if (!project) {
261+
this.logger.debug('Project not found. (targetId=%s)', args.reference.byId);
262+
return null;
263+
}
264+
265+
selector = {
266+
organizationId: project.orgId,
267+
projectId: project.id,
268+
};
269+
}
270+
271+
this.logger.debug(
272+
'Project selector resolved. (organization=%s, project=%s)',
273+
selector.organizationId,
274+
selector.projectId,
275+
);
276+
277+
return selector;
278+
}
221279
}
222280

223281
function filterSelector(

packages/services/api/src/modules/shared/providers/storage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ export interface Storage {
205205

206206
getProjects(_: OrganizationSelector): Promise<Project[] | never>;
207207

208+
getProjectById(projectId: string): Promise<Project | null>;
209+
208210
findProjectsByIds(args: { projectIds: Array<string> }): Promise<Map<string, Project>>;
209211

210212
createProject(_: Pick<Project, 'type'> & { slug: string } & OrganizationSelector): Promise<

packages/services/storage/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,11 @@ export async function createStorage(
14301430
});
14311431
},
14321432
),
1433+
getProjectById(projectId) {
1434+
return this.findProjectsByIds({ projectIds: [projectId] }).then(
1435+
map => map.get(projectId) ?? null,
1436+
);
1437+
},
14331438
async updateProjectSlug({ slug, organizationId: organization, projectId: project }) {
14341439
return pool.transaction(async t => {
14351440
const projectSlugExists = await t.exists(

packages/web/app/src/components/project/settings/external-composition.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ const ExternalComposition_DisableMutation = graphql(`
307307

308308
const ExternalComposition_ProjectConfigurationQuery = graphql(`
309309
query ExternalComposition_ProjectConfigurationQuery($selector: ProjectSelectorInput!) {
310-
project(selector: $selector) {
310+
project(reference: { bySelector: $selector }) {
311311
id
312312
slug
313313
isNativeFederationEnabled

packages/web/app/src/components/project/settings/native-composition.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ const NativeCompositionSettings_ProjectFragment = graphql(`
117117

118118
const NativeCompositionSettings_ProjectQuery = graphql(`
119119
query NativeCompositionSettings_ProjectQuery($selector: ProjectSelectorInput!) {
120-
project(selector: $selector) {
120+
project(reference: { bySelector: $selector }) {
121121
id
122122
nativeFederationCompatibility
123123
experimental_nativeCompositionPerTarget

0 commit comments

Comments
 (0)