回傳的資料物件如下所示:
{
data: {
posts: {
edges: [
{
post: {
id: "1",
title: "Foo"
}
},
{
post: {
id: "2",
title: "Bar"
}
}
]
}
}
}
這基于以下查詢:
query MyQuery {
posts {
edges {
post: node {
id
title
}
}
}
}
這行得通,我可以使用它,但不幸的是,我不得不創建嵌套介面。
問題:我可以簡化回傳的結果還是用 JavaScript 轉換它們map()?
理想情況下,我希望 GQL 回應(或結果物件)如下所示:
{
data: {
posts: [
{
id: "1",
title: "Foo"
},
{
id: "2",
title: "Bar"
}
]
}
}
注意:我無法更新服務器端 GraphQL 架構。解決方案必須是客戶端/消費者端。
謝謝!
編輯
添加呼叫和處理 GraphQL 的 Angular/TS 代碼...
post.service.ts
import { Injectable } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
import { map, Observable } from 'rxjs';
import { GraphQLResponse } from 'src/app/core/types/graphQLResponse';
import { Post } from '../models/post';
export interface PostResponse {
edges: Post[]
pageInfo: {
startCursor: string
hasPreviousPage: boolean
hasNextPage: boolean
endCursor: string
}
}
export const getPostsQuery = gql`
query getPostsQuery {
posts {
edges {
post: node {
id
title
date
uri
categories {
edges {
category: node {
id
name
uri
}
}
}
}
cursor
}
pageInfo {
startCursor
hasPreviousPage
hasNextPage
endCursor
}
}
}
`;
@Injectable({
providedIn: 'root'
})
export class PostService {
constructor(private apollo: Apollo) { }
public getPosts(): Observable<PostResponse> {
return this.apollo.query<GraphQLResponse<'posts', PostResponse>>({
query: getPostsQuery
}).pipe(map(resp => resp.data.posts));
}
}
模型/post.ts
interface CategoryNode {
id: string;
name: string;
uri: string;
}
interface Category {
category: CategoryNode;
}
interface CategoryEdges{
edges: Category[];
}
interface PostNode {
id: string;
title: string;
date: string;
uri: string;
categories: CategoryEdges;
}
export interface Post {
article: PostNode;
cursor: string;
}
如您所見,嵌套介面太多了。
實際樣本回應(用于單元測驗)
{
data: {
posts: {
edges : [
{
post: {
id: "cG9zdDoxMjc=",
title: "Lorem Ipsum",
date: "2022-01-06T22:00:53",
uri: "\/2022\/01\/06\/lorem-ipsum\/",
categories: {
edges: [
{
category: {
id: "dGVybToy",
name: "General",
uri: "\/category\/general\/"
}
}
]
}
},
cursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
},
{
post: {
id: "cG9zdDoxMjc=",
title: "Lorem Ipsum",
date: "2022-01-06T22:00:53",
uri: "\/2022\/01\/06\/lorem-ipsum\/",
categories: {
edges: [
{
category: {
id: "dGVybToy",
name: "General",
uri: "\/category\/general\/"
}
},
{
category: {
id: "dGVybToy",
name: "General",
uri: "\/category\/general\/"
}
}
]
}
},
cursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
},
],
pageInfo: {
startCursor: "YXJyYXljb25uZWN0aW9uOjEyNw==",
hasPreviousPage: false,
hasNextPage: false,
endCursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
}
}
}
};
uj5u.com熱心網友回復:
如果無法自省 GQL 架構,很難就如何修改查詢以獲得所需的形狀(如果可能的話)向您提供建議,但無需修改查詢,您就可以將回應值轉換為您想要的形狀想要這樣:
TS游樂場
interface PostNode {
id: string;
title: string;
}
interface PostEdge { post: PostNode; }
type GQLResponse<T> = { data: T; };
type PostsResponse = GQLResponse<{
posts: {
edges: PostEdge[];
};
}>;
type TransformedPostsResponse = {
data: {
posts: PostNode[];
};
};
function transformPostsResponse (res: PostsResponse): TransformedPostsResponse {
const result: TransformedPostsResponse = {data: {posts: []}};
for (const edge of res.data.posts.edges) result.data.posts.push(edge.post);
return result;
}
const postsResponse: PostsResponse = {
data: {
posts: {
edges: [
{
post: {
id: "1",
title: "Foo"
}
},
{
post: {
id: "2",
title: "Bar"
}
}
]
}
}
};
const result = transformPostsResponse(postsResponse);
console.log(result);
演示(從 TS Playground 編譯的 JS):
顯示代碼片段
"use strict";
function transformPostsResponse(res) {
const result = { data: { posts: [] } };
for (const edge of res.data.posts.edges)
result.data.posts.push(edge.post);
return result;
}
const postsResponse = {
data: {
posts: {
edges: [
{
post: {
id: "1",
title: "Foo"
}
},
{
post: {
id: "2",
title: "Bar"
}
}
]
}
}
};
const result = transformPostsResponse(postsResponse);
console.log(result);
uj5u.com熱心網友回復:
我最終使用嵌套的map()'ing 將 GraphQL 回應轉換為“更干凈”的物件。
如果有人有同樣的問題/問題,下面是我的最終代碼。
注意:在下面的代碼中,我使用的是“文章”而不是“帖子”,但這是同一個概念。
模型/文章-gql.ts
interface GqlCategoryNode {
category: {
id: string;
name: string;
uri: string;
};
}
interface GqlArticleNode {
article: {
id: string;
title: string;
date: string;
uri: string;
categories: {
edges: GqlCategoryNode[]
};
};
cursor: string;
}
export interface GqlArticleResponse {
edges: GqlArticleNode[]
pageInfo: {
startCursor: string
hasPreviousPage: boolean
hasNextPage: boolean
endCursor: string
}
}
模型/article.ts
interface Category {
id: string;
name: string;
uri: string;
}
export interface Article {
id: string;
title: string;
date: string;
uri: string;
categories: Category[];
cursor: string;
}
export interface PageInfo {
startCursor: string;
hasPreviousPage: boolean;
hasNextPage: boolean;
endCursor: string;
}
文章.service.ts
import { Injectable } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
import { map, Observable } from 'rxjs';
import { GraphQLResponse } from 'src/app/core/types/graphQLResponse';
import { Article, PageInfo } from '../models/article';
import { GqlArticleResponse } from '../models/article-gql';
export const getArticlesQuery = gql`
query getArticlesQuery {
articles: posts {
edges {
article: node {
id
title
date
uri
categories {
edges {
category: node {
id
name
uri
}
}
}
}
cursor
}
pageInfo {
startCursor
hasPreviousPage
hasNextPage
endCursor
}
}
}
`;
@Injectable({
providedIn: 'root'
})
export class ArticleService {
constructor(private apollo: Apollo) { }
public getArticles(): Observable<[PageInfo, Article[]]> {
return this.apollo.query<GraphQLResponse<'articles', GqlArticleResponse>>({
query: getArticlesQuery
}).pipe(map(resp => {
return [
resp.data.articles.pageInfo as PageInfo,
resp.data.articles.edges.map((articleNode) => {
return {
id: articleNode.article.id,
title: articleNode.article.title,
date: articleNode.article.date,
uri: articleNode.article.uri,
cursor: articleNode.cursor,
categories: articleNode.article.categories.edges.map((categoryNode) => {
return {
id: categoryNode.category.id,
name: categoryNode.category.name,
uri: categoryNode.category.uri
}
})
}
})]
})) as Observable<[PageInfo, Article[]]>;
}
}
article.service.spec.ts
下面您會注意到我正在轉換服務中的服務器回應并測驗來自服務的回應以確保它按預期進行轉換。
import { TestBed } from '@angular/core/testing';
import { Apollo } from 'apollo-angular';
import { ApolloTestingController, ApolloTestingModule } from 'apollo-angular/testing';
import { Article, PageInfo } from '../models/article';
import { GqlArticleResponse } from '../models/article-gql';
import { ArticleService, getArticlesQuery } from './article.service';
describe('ArticleService', () => {
let service: ArticleService;
let controller: ApolloTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
ApolloTestingModule,
],
providers: [
ArticleService
]
});
service = TestBed.inject(ArticleService);
controller = TestBed.inject(ApolloTestingController);
});
afterEach(async () => {
const apolloClient = TestBed.inject(Apollo).client;
await apolloClient.clearStore();
})
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should return a list of articles', (done) => {
const mockArticlesServerResponse: GqlArticleResponse = {
edges: [
{
article: {
id: "cG9zdDoxMjc=",
title: "Lorem Ipsum",
date: "2022-01-06T22:00:53",
uri: "\/2022\/01\/06\/lorem-ipsum\/",
categories: {
edges: [
{
category: {
id: "dGVybToy",
name: "General",
uri: "\/category\/general\/"
}
}
]
}
},
cursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
},
{
article: {
id: "cG9zdDoxMjc=",
title: "Lorem Ipsum",
date: "2022-01-06T22:00:53",
uri: "\/2022\/01\/06\/lorem-ipsum\/",
categories: {
edges: [
{
category: {
id: "dGVybToy",
name: "General",
uri: "\/category\/general\/"
}
},
{
category: {
id: "dGVybToy",
name: "Something",
uri: "\/category\/general\/"
}
}
]
}
},
cursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
}
],
pageInfo: {
startCursor: "YXJyYXljb25uZWN0aW9uOjEyNw==",
hasPreviousPage: false,
hasNextPage: false,
endCursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
}
};
const mockArticlesServiceResponse: [PageInfo, Article[]] = [
{
startCursor: "YXJyYXljb25uZWN0aW9uOjEyNw==",
hasPreviousPage: false,
hasNextPage: false,
endCursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
},
[
{
id: "cG9zdDoxMjc=",
title: "Lorem Ipsum",
date: "2022-01-06T22:00:53",
uri: "\/2022\/01\/06\/lorem-ipsum\/",
categories: [
{
id: "dGVybToy",
name: "General",
uri: "\/category\/general\/"
}
],
cursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
},
{
id: "cG9zdDoxMjc=",
title: "Lorem Ipsum",
date: "2022-01-06T22:00:53",
uri: "\/2022\/01\/06\/lorem-ipsum\/",
categories: [
{
id: "dGVybToy",
name: "General",
uri: "\/category\/general\/"
},
{
id: "dGVybToy",
name: "Something",
uri: "\/category\/general\/"
}
],
cursor: "YXJyYXljb25uZWN0aW9uOjEyNw=="
}
]
];
service.getArticles().subscribe(resp => {
expect(resp).toEqual(mockArticlesServiceResponse);
done();
});
const req = controller.expectOne(getArticlesQuery);
expect(req.operation.operationName).toBe('getArticlesQuery');
req.flush({ data: { articles: mockArticlesServerResponse } });
controller.verify();
});
});
感謝大家的投入和幫助!
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/405409.html
標籤:
