Apollo-link: рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐ рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреНрд╖реЗрддреНрд░ рдЕрдкрд░рд┐рднрд╛рд╖рд┐рдд рд╣реИ [рдЕрдкреЛрд▓реЛ-рд▓рд┐рдВрдХ-рддреНрд░реБрдЯрд┐]

рдХреЛ рдирд┐рд░реНрдорд┐рдд 6 рдирд╡ре░ 2018  ┬╖  29рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: apollographql/apollo-link


рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ onError рдлрд╝рдВрдХреНрд╢рди рдпрд╛ рдЗрд╕рдХреЗ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдореЗрдВ рдХреЛрдИ рдмрдЧ рд╣реИред рдЗрд╕ рдореБрджреНрджреЗ рдХрд╛ рдкрд╣рд▓реЗ рд╣реА #698 рдореЗрдВ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЬрд╛ рдЪреБрдХрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВрдиреЗ рд╕реЛрдЪрд╛ рдХрд┐ рд╕рдорд╛рдзрд╛рди рдХреА рдЙрдореНрдореАрдж рдореЗрдВ рдореИрдВ рдЗрд╕реЗ рдереЛрдбрд╝рд╛ рд╕рд╛ рд╕рдордЭреВрдВрдЧрд╛ред

onError рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдпрд╛ рддреЛ apollo-boost рд╕реЗ рдпрд╛ рд╕реАрдзреЗ apollo-link-error $ рд╕реЗ рдХрд┐рд╕реА рддреНрд░реБрдЯрд┐ рдХреЛ рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯ рдХрд░рддреЗ рд╕рдордп рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдореЗрдВ рдХрд╣рд╛ рдЧрдпрд╛ рд╣реИ рдХрд┐ рддреНрд░реБрдЯрд┐ рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ response рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдЕрдирджреЗрдЦрд╛ рдХрд░реЗрдВред

рдореИрдВрдиреЗ рдбреЙрдХреНрд╕ рдЪреЗрдХрдмреЙрдХреНрд╕ рдХреЛ рдЪрд┐рд╣реНрдирд┐рдд рдХрд┐рдпрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЗрдЪреНрдЫрд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЬреИрд╕рд╛ рдХрд┐ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ networkError рдЕрдиреБрднрд╛рдЧ рдХреЗ рдЙрдк-рдкреИрд░рд╛рдЧреНрд░рд╛рдл рдореЗрдВ response рдСрдмреНрдЬреЗрдХреНрдЯ рдХрд╛ рдЙрд▓реНрд▓реЗрдЦ рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕реЗ рд╢рд╛рдпрдж рдереЛрдбрд╝рд╛ рд╕рд╛ рд╕реНрдкрд╖реНрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдпрджрд┐ рд╡рд╣ рд╣реИ рдореБрдХрджрдорд╛ред

рдЕрдкреЗрдХреНрд╖рд┐рддреН рд╡реНрдпрд╡рд╣рд╛рд░
response.errors = null; рд╕реЗрдЯ рдХрд░рдиреЗ рд╕реЗ рддреНрд░реБрдЯрд┐ рдХреЛ рджреВрд╕рд░реА рддреНрд░реБрдЯрд┐ рдлреЗрдВрдХреЗ рдмрд┐рдирд╛ рдкреНрд░рдЪрд╛рд░рд┐рдд рд╣реЛрдиреЗ рд╕реЗ рд░реЛрдХрдирд╛ рдЪрд╛рд╣рд┐рдПред

рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реНрдпрд╡рд╣рд╛рд░
networkError рдХреЛ рдкрдХрдбрд╝рддреЗ рд╕рдордп response рдлрд╝реАрд▓реНрдб рдЕрдкрд░рд┐рднрд╛рд╖рд┐рдд рд╣реЛрддреА рд╣реИ, рдЬрд┐рд╕рд╕реЗ response.errors рд╕реЗрдЯ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рддреЗ рд╕рдордп рд╕рд┐рдВрдЯреИрдХреНрд╕ рддреНрд░реБрдЯрд┐рдпрд╛рдВ рд╣реЛрддреА рд╣реИрдВред рдЙрди рдХрд╛рд░рдгреЛрдВ рдХреЗ рд▓рд┐рдП рдЬреЛ рдЗрд╕ рд╕рдордп рдореЗрд░реЗ рдмрд╛рд╣рд░ рд╣реИрдВ, рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐ рдХреЛ рдлреИрд▓рдиреЗ рд╕реЗ рд░реЛрдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдФрд░ рддреНрд░реБрдЯрд┐ рдлреЗрдВрдХрддрд╛ рд╣реИред

рдореЗрд░реЗ рд╕реЗрдЯрдЕрдк рдореЗрдВ (MacOS 10.14.1 w. Chrome 70.0.3538.77) рд╕реЗрдЯрд┐рдВрдЧ response.errors = null рдПрдХ Uncaught TypeError: Cannot set property 'errors' of undefined рдлреЗрдВрдХ рджреЗрдЧреА рдФрд░ response = { errors: null } рдПрдХ Uncaught Error: "response" is read-only рдлреЗрдВрдХ рджреЗрдЧреАред

skaermbillede 2018-11-06 kl 15 08 10

skaermbillede 2018-11-06 kl 15 09 20

рдПрдХ _рд╕рд░рд▓_ рдкреНрд░рдЬрдирди

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд apollo-boost рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдкреБрдирд░реБрддреНрдкрд╛рджрди рд╣реИред рд╡рд╣реА рд╕рдорд╕реНрдпрд╛ рд╕реАрдзреЗ apollo-link-error рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдкрд░ рд╣реЛрддреА рд╣реИред

import ApolloClient from 'apollo-boost';

const client = ApolloClient({
  uri: '/my_api',
  onError: (({ response, networkError }) => {
    if (networkError && networkError.statusCode === 401) {
      console.log(response); // prints 'undefined'
      response.errors = null; // will throw 'undefined' error
      response = { errors: null }; // will throw a 'read-only' error 
    }
  })
});

рдЕрдВрдХ рд▓реЗрдмрд▓

  • [] рд╣реИ-рдкреНрд░рдЬрдирди
  • [ ] рд╡рд┐рд╢реЗрд╖рддрд╛
  • [рдПрдХреНрд╕] рджрд╕реНрддрд╛рд╡реЗрдЬрд╝
  • [] рдЕрд╡рд░реБрджреНрдз
  • [] рдЕрдЪреНрдЫрд╛ рдкрд╣рд▓рд╛ рдЕрдВрдХ

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рдЗрд╕ рдореБрджреНрджреЗ рдкрд░ рдХреЛрдИ рдкреНрд░рдЧрддрд┐?

рд╕рднреА 29 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдХреНрдпрд╛ рдЗрд╕ рдмрдЧ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдЕрд╕реНрдерд╛рдпреА рд╕рдорд╛рдзрд╛рди рд╣реИ?

+1 рдбрд┐рдмрдЧрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдПрдХ рдмрд╣реБрдд рдмрдбрд╝реА рд╕рдорд╕реНрдпрд╛ рд╣реИ рдпрджрд┐ рдЖрдк рд╕рд░реНрд╡рд░ рд╡рд╛рддрд╛рд╡рд░рдг рдореЗрдВ рдЕрдкреЛрд▓реЛ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВ (рдЕрдкрдиреЗ рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрд▓рдЧ рдЧреНрд░рд╛рдлрд╝рдХреНрдпреВрдПрд▓ рд╕рд░реНрд╡рд░ рд╕реЗ рдбреЗрдЯрд╛ рдкрдврд╝рдирд╛)ред

+1
рдХреНрдпрд╛ рд╕рд╢рд░реНрдд рд░реВрдк рд╕реЗ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдЕрдирджреЗрдЦрд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рд╕рдорд╛рдзрд╛рди рд╣реИ?

рдореЗрд░реЗ рдкрд╛рд╕ рднреА рдпрд╣ рдореБрджреНрджрд╛ рд╣реИред

рдореЗрд░реЗ рд▓рд┐рдП рдмрдбрд╝реА рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ UI рдХреЛ рдЗрд╕ рддреНрд░реБрдЯрд┐ рд╕реЗ рдЕрд╡рдЧрдд рд╣реЛрдиреЗ рд╕реЗ рд░реЛрдХрддрд╛ рд╣реИред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреЛрдИ рдмрдбрд╝реА рддреНрд░реБрдЯрд┐ рд╣реЛрдиреЗ рдкрд░ рдпрд╣ рд▓реЛрдбрд┐рдВрдЧ рд╕реНрдерд┐рддрд┐ рдкрд░ рд░рд╣рддрд╛ рд╣реИред

рдореИрдВрдиреЗ рдЗрд╕ рдкрд░ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рд╣реИ рдФрд░ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реЛрдВ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рдХреЙрд▓рдмреИрдХ рдХреЛ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреА рдЬрд╛рдиреА рдЪрд╛рд╣рд┐рдП:
https://www.apollographql.com/docs/link/links/error.html#callback

рд╕рд╛рдорд╛рдиреНрдп рдХреЙрд▓рдмреИрдХ рдХреЗ рд╕рд╛рде рдРрд╕рд╛ рд╣реЛрддрд╛ рд╣реИ: https://github.com/apollographql/apolo-link/blob/master/packages/apolo-link-error/src/index.ts#L46

рд▓реЗрдХрд┐рди рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐ рдХреЗ рд╕рд╛рде: https://github.com/apollographql/apollo-link/blob/master/packages/apolo-link-error/src/index.ts#L62

рдЗрд╕рдХрд╛ рд╡рд┐рд▓рдп рдХрдм рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ рдЗрд╕ рдкрд░ рдХреЛрдИ рдЕрдВрддрд░реНрджреГрд╖реНрдЯрд┐? :)

рд╕рдВрднрд╡рдд: рдмрд╣реБрдд рдЬрд▓реНрдж, рдЕрдкреЛрд▓реЛ рдЯреАрдо рдирдП рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рдЬрд╛рд░реА рдХрд░рдиреЗ рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░ рд░рд╣реА рд╣реИред

рдЗрд╕ рдореБрджреНрджреЗ рдкрд░ рдХреЛрдИ рдкреНрд░рдЧрддрд┐?

рдореИрдВ рдпрд╣ рднреА рд╕реЛрдЪ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдХреНрдпрд╛ рдЗрд╕ рдкрд░ рдХреЛрдИ рдкреНрд░рдЧрддрд┐ рд╣реБрдИ рд╣реИ? рдпрд╛ рдЗрд╕ рдмреАрдЪ рдХреЛрдИ рдЙрдкрд╛рдп? рдореИрдВ рдПрдкреАрдЖрдИ рд╕реЗ рдпреВрдЖрдИ рдореЗрдВ рдХрд╕реНрдЯрдо рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рдкреНрд░рдЪрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХрд╛ рдПрдХ рддрд░реАрдХрд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред

рдореИрдВ рднреА рдЗрд╕реА рдореБрджреНрджреЗ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░ рд░рд╣рд╛ рдерд╛ред рдореЗрд░реЗ рд▓рд┐рдП рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб рдЦрд╛рд▓реА рдСрдмреНрдЬрд░реНрд╡реЗрдмрд▓ рд╡рд╛рдкрд╕ рдХрд░рдирд╛ рдерд╛ред

рдЕрдЧрд░ рд╕рд░реНрд╡рд░ 503 рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИ - рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХрдиреЗ рдХреЗ рдмрд┐рдирд╛ рдореИрдВ рдЪреБрдкрдЪрд╛рдк рд░рдЦрд░рдЦрд╛рд╡ рдкреГрд╖реНрда рдкрд░ рд░реАрдбрд╛рдпрд░реЗрдХреНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ред рдпрд╣рд╛рдБ рдореЗрд░рд╛ рдХреЛрдб рд╣реИ, рд╢рд╛рдпрдж рдпрд╣ рдХрд┐рд╕реА рдХреА рдорджрдж рдХрд░реЗрдЧрд╛:

import { Observable } from 'apollo-link';

...

onError(({ networkError }: any) => {
      if (networkError && networkError.status === 503) {
        this.redirectService.goToMaintenance();
        return Observable.of();
      }
});

...

@ luki215 рдореЗрд░реЗ рдкрд╛рд╕ рдХреЛрдИ рддреНрд░реБрдЯрд┐ рдХреЛрдб рдлрд╝реАрд▓реНрдб рдирд╣реАрдВ рд╣реИ
image

@ рдЖрдк рдХрд┐рд╕ apollo-link рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ? рдореИрдВ apollo-angular-link-http рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВ, рддрд╛рдХрд┐ рдпрд╣ рдЕрдВрддрд░ рд╣реЛред

@ luki215 рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдореЗрд░реА рдЧрд▓рддреА рдереА, рдПрдбрдмреНрд▓реНрдпреВрдПрд╕ рдПрдкреАрдЖрдИ рдЧреЗрдЯрд╡реЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдореИрдВрдиреЗ рдХрд┐рд╕реА рдФрд░ рдЪреАрдЬ рдХреЗ рд▓рд┐рдП рд╕реАрдУрдЖрд░рдПрд╕ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛ рдареАрдХ рд╕реЗ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИ рдХрд┐ 200 рдХреЛрдб

рд╕рдорд╕реНрдпрд╛ рдореЗрдВ рдЪрд▓ рд░рд╣реЗ рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдЖрдк рдбрд┐рдлрд╝реЙрд▓реНрдЯ 4xx рдореЗрдВ рдПрдкреАрдЖрдИ рдЧреЗрдЯрд╡реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдУрдВ рдкрд░ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ:
image

рдпрд╛ serverless.yml . рдореЗрдВ
yml GatewayDefault4XX: Type: 'AWS::ApiGateway::GatewayResponse' Properties: ResponseParameters: gatewayresponse.header.Access-Control-Allow-Origin: "'*'" gatewayresponse.header.Access-Control-Allow-Headers: "'*'" ResponseType: DEFAULT_4XX RestApiId: Ref: 'ApiGatewayRestApi'

рдореЗрд░рд╛ рдорд╛рдирдирд╛ тАЛтАЛтАЛтАЛрд╣реИ рдХрд┐ рдореИрдВ рдПрдХ рд╣реА рдареЛрдХрд░ рдЦрд╛ рд░рд╣рд╛ рд╣реВрдВ рдФрд░ рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдореЗрд░реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рдЕрдЧреНрд░реЗрд╖рд┐рдд рдХрд░рдиреЗ рдореЗрдВ рдЕрд╕рдорд░реНрде рд╣реВрдВред рдХреНрдпрд╛ рдЗрд╕ рдореБрджреНрджреЗ рдХреЗ рд╕рдорд╛рдзрд╛рди рдкрд░ рдХреЛрдИ рдкреНрд░рдЧрддрд┐ рд╣реБрдИ рд╣реИ?

@strass рдЗрд╕ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреНрд╖реЗрддреНрд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкрддрд╛ рдирд╣реАрдВ рд╣реИ рд▓реЗрдХрд┐рди рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдк рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐ рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ

export default onError(({ graphQLErrors, networkError }) => {
    if (graphQLErrors) {
        for (let { extensions: { code } } of graphQLErrors)
            handleTheError(code)
    }
    if (networkError) {
        switch (networkError.statusCode) {
            case 500:
                //
                break
            case 429:
                //
                break
            case undefined:
                //
                break
            default:
                console.error(networkError)
                break
        }
    }
})

рдПрдХ рдЦрд╛рд▓реА рдСрдмреНрдЬрд░реНрд╡реЗрдмрд▓ рдХреЛ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╕ @ luki215 рдХреЗ рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб рдХреА рдХреЛрд╢рд┐рд╢ рдХреА, рд▓реЗрдХрд┐рди рдореЗрд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдЬреЛ рдЕрдм рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛-рдЕрдкреЛрд▓реЛ рдХреЛ рдПрдХ рдЕрдкрд╡рд╛рдж рдмрдирд╛ рджреЗрдЧрд╛:

Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
    at Mutation._this.onMutationCompleted (react-apollo.esm.js:627)
    at react-apollo.esm.js:564

рдореИрдВ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ Mutation . рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдШрдЯрдХ рдореЗрдВ рд▓реМрдЯрд╛рдИ рдЧрдИ рддреНрд░реБрдЯрд┐ рдХреЛ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░ рд░рд╣рд╛ рд╣реВрдВ

const errorLink = onError(({ forward, networkError, operation }) => {
  if (networkError) {
    const { message: errorMessage } = networkError.result;
    switch (networkError.statusCode) {
      case 400:
        if (errorMessage) {
          networkError.message = errorMessage;
        }
        break;
      case 403:
        window.location = '/users/login';
        break;
      default:
        break;
    }
  }
  forward(operation);
});

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд▓реЛрдЧ рдпрд╣рд╛рдВ рдЬреЛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдЪреАрдЬ рдвреВрдВрдв рд░рд╣реЗ рд╣реИрдВ рд╡рд╣ рд╣реИ networkError рдореЗрдВ рд▓реМрдЯрд╛рдИ рдЧрдИ рддреНрд░реБрдЯрд┐ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЛ рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдФрд░ рдпрд╣ рдЗрд╕ networkError.message = 'Some custom error message.' рдХреЗ рд╕рд╛рде рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ

apollo-link-rest рдФрд░ рдЗрд╕ рдкреИрдХреЗрдЬ рдореЗрдВ рдЦреБрджрд╛рдИ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдПрдХ рд╣реИрдХреА рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб рдХреЗ рд╕рд╛рде рдЖрдпрд╛ред onError рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рдореИрдВрдиреЗ рдЕрдкрдирд╛ рдЦреБрдж рдХрд╛ (рдиреАрдЪреЗ) рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦрд╛ред

рдЖрд╢рдп: рдПрдХ NetworkError рдХреЛ рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯ рдХрд░реЗрдВ рдФрд░ рд╕рд╢рд░реНрдд рд░реВрдк рд╕реЗ рдЗрд╕реЗ GraphQLError рдореЗрдВ рдмрджрд▓ рджреЗрдВред рдХрд╛рд░рдг, рдореИрдВ рдПрдХ рдЖрд░рдИрдПрд╕рдЯреА рдПрдВрдбрдкреЙрдЗрдВрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдЬреЛ рдЧреНрд░рд╛рдлрдХреНрдпреВрдПрд▓ рд╕рдореНрдореЗрд▓рди рдХрд╛ рдкрд╛рд▓рди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рдФрд░ 400 рд╕реНрдЯреЗрдЯрд╕ рдХреЛрдб рдХреЗ рд╕рд╛рде {"message": "{ \"key\": \"not valid\"}"} рдЬреИрд╕реЗ рдЖрдХрд╛рд░ рджреЗрддрд╛ рд╣реИред рдЗрд╕реЗ NetworkError рдХреЗ рд░реВрдк рдореЗрдВ рдирд╣реАрдВ рдорд╛рдирд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред

рдХреНрдпрд╛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ: рдпрд╣ рд╕рдорд╛рдзрд╛рди рд╢реНрд░реГрдВрдЦрд▓рд╛ рдХреЗ рдиреАрдЪреЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕реЗ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рдХреЛ рдкрд╛рд╕ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕ рддрд░рд╣ рд╕реЗ рдореИрдВ рдЗрд╕реЗ mutate рд╡рд╛рджреЗ catch рдХреЗ рднреАрддрд░ рдкреБрдирд░реНрдкреНрд░рд╛рдкреНрдд рдФрд░ рдкрд╛рд░реНрд╕ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рдФрд░ рдмрдЪреНрдЪреЗ рдХреЗ рд░реВрдк рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ .

рдХреНрдпрд╛ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ:

  • рдореИрдВ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рд╕рд╣реА рддрд░реАрдХреЗ рд╕реЗ рдкрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП observer.next({ data: { ... }, errors: { ... } }) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдкрд╕рдВрдж рдХрд░реВрдВрдЧрд╛, рд▓реЗрдХрд┐рди рдпрд╣ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ (рд╡рд╣ рдбреЗрдЯрд╛ рдХрд╣реАрдВ рдирд╣реАрдВ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рд╡рд╛рджрд╛ рд╣рд▓ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ)ред
  • рдЗрд╕ рдХреНрд░рдо рдореЗрдВ observer.next() рдФрд░ observer.error() рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рдореЗрд░реЗ рджреНрд╡рд╛рд░рд╛ рдЕрдкреЗрдХреНрд╖рд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ ред рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдХреЗрд╡рд▓ рддреНрд░реБрдЯрд┐ рдХреЙрд▓ рдкреНрд░рднрд╛рд╡реА рд╣реЛрддреА рд╣реИред
const errorLink = new ApolloLink((operation, forward) => {
    return new Observable(observer => {
      let sub: ZenObservable.Subscription;

      try {
        sub = forward!(operation).subscribe({
          next: result => {
            console.log("A proper result looks like:", result);
            observer.next(result)
          },
          error: networkError => {
            try {
              const { message } = networkError.result;
              const error = new GraphQLError(message);
              const apolloError = new ApolloError({
                errorMessage: "Some error happened",
                graphQLErrors: [error],
                networkError: undefined,
              });

              observer.error(new Error(message));
            } catch (_) {
              observer.error(networkError)
            }
          },
          complete: () => {
            console.log("Calling complete()");
            observer.complete();
          },
        });
      } catch (e) {
        console.log("ErrorLink had an error of its own")
        observer.error(e);
      }

      return () => {
        if (sub) sub.unsubscribe();
      };
    });
  });

рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП, рд▓реЗрдХрд┐рди mutate() рдлрд╝рдВрдХреНрд╢рди рд╣рд▓ рдпрд╛ рдЕрд╕реНрд╡реАрдХрд╛рд░ рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рд╣реИ - рди рддреЛ then() рдФрд░ рди рд╣реА catch() рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред

const errorLink = new ApolloLink((operation, forward) => {
    return new Observable(observer => {
      let sub: ZenObservable.Subscription;

      try {
        sub = forward!(operation).subscribe({
          next: result => {
            console.log("A proper result looks like:", result);
            observer.next(result)
          },
          error: networkError => {
            try {
              const { message } = networkError.result;
              const error = new GraphQLError(message);

              // this is called, but it seems to be the end of the line
              console.log("Intercepting error, returning success");
              observer.next({ data: {}, errors: [ error ]});
            } catch (localError) {
              console.error("Error in link:", localError);
              observer.error(networkError)
            }
          },
          complete: () => {
            console.log("Calling complete()");
            observer.complete();
          },
        });
      } catch (e) {
        console.log("ErrorLink had an error of its own")
        observer.error(e);
      }

      return () => {
        if (sub) sub.unsubscribe();
      };
    });
  });

рдмрд╕ рдзрдХреНрдХрд╛ рджреЗ рд░рд╣рд╛ рд╣реИ...
рдЕрдм рдЙрд╕реА рддреНрд░реБрдЯрд┐ рдкрд░ рдареЛрдХрд░ рдЦрд╛ рд░рд╣рд╛ рд╣реИред
рдШрдЯрдХ рдкрд░ рд╣реА рддреНрд░реБрдЯрд┐ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рддреНрд░реБрдЯрд┐ рдХреЛ рд░реАрд╕реЗрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ ...

рдХреЛрдЗ рдкреНрд░рдЧрддрд┐?

рдПрдХ рд╣реА рдореБрджреНрджреЗ рдкрд░ +1ред

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдореБрдЭреЗ рдПрдХ рд╕рдорд╛рдзрд╛рди рдорд┐рд▓ рдЧрдпрд╛ рд╣реЛрдЧрд╛ (рдХрдо рд╕реЗ рдХрдо рдореЗрд░реА рддреНрд░реБрдЯрд┐ рдкреНрд░рд╕рд╛рд░ рд╕рдорд╕реНрдпрд╛ рдХреЗ рд▓рд┐рдП):

рддреНрд░реБрдЯрд┐ рд▓рд┐рдВрдХ рдореЗрдВ map рдХреЛ forEach рд╕реЗ рдмрджрд▓рдиреЗ рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рддреНрд░реБрдЯрд┐рдпрд╛рдВ UI рдореЗрдВ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХрд╛ рдкреНрд░рд╕рд╛рд░ рдХрд░ рд╕рдХрддреА рд╣реИрдВ (рдкрд╣рд▓реЗ рдкрд░рд┐рдгрд╛рдоред UI рдШрдЯрдХ рдореЗрдВ рддреНрд░реБрдЯрд┐ рдЕрдкрд░рд┐рднрд╛рд╖рд┐рдд рдереА, рдЕрдм рдЗрд╕рдореЗрдВ рддреНрд░реБрдЯрд┐ рд╣реИ):

const link = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
-    graphQLErrors.map(({ message, locations, path }) =>
+    graphQLErrors.forEach(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
      ),
    );

  if (networkError) console.log(`[Network error]: ${networkError}`);
});

рдореИрдВ рднреА рдЗрд╕реА рдореБрджреНрджреЗ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░ рд░рд╣рд╛ рдерд╛ред рдореЗрд░реЗ рд▓рд┐рдП рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб рдЦрд╛рд▓реА рдСрдмреНрдЬрд░реНрд╡реЗрдмрд▓ рд╡рд╛рдкрд╕ рдХрд░рдирд╛ рдерд╛ред

рдЕрдЧрд░ рд╕рд░реНрд╡рд░ 503 рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИ - рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХрдиреЗ рдХреЗ рдмрд┐рдирд╛ рдореИрдВ рдЪреБрдкрдЪрд╛рдк рд░рдЦрд░рдЦрд╛рд╡ рдкреГрд╖реНрда рдкрд░ рд░реАрдбрд╛рдпрд░реЗрдХреНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ред рдпрд╣рд╛рдБ рдореЗрд░рд╛ рдХреЛрдб рд╣реИ, рд╢рд╛рдпрдж рдпрд╣ рдХрд┐рд╕реА рдХреА рдорджрдж рдХрд░реЗрдЧрд╛:

import { Observable } from 'apollo-link';

...

onError(({ networkError }: any) => {
      if (networkError && networkError.status === 503) {
        this.redirectService.goToMaintenance();
        return Observable.of();
      }
});

...

рдЗрд╕рдиреЗ рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд┐рдпрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдХреЗ рдмрд┐рд▓реНрдХреБрд▓ рд╡рд┐рдкрд░реАрдд рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реИ, рдпрд╛рдиреА "рддреНрд░реБрдЯрд┐ рдХреЙрд▓рдмреИрдХ рд╡реИрдХрд▓реНрдкрд┐рдХ рд░реВрдк рд╕реЗ рдЕрдиреБрд░реЛрдз рдХреЛ рдкреБрдирдГ рдкреНрд░рдпрд╛рд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдЧреЗ (рдСрдкрд░реЗрд╢рди) рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рдПрдХ рдЕрд╡рд▓реЛрдХрди рдпреЛрдЧреНрдп рд╡рд╛рдкрд╕ рдХрд░ рд╕рдХрддрд╛ рд╣реИред рдЗрд╕реЗ рдХреБрдЫ рдФрд░ рд╡рд╛рдкрд╕ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред"

рдЖрдЬ рдЕрдкреЛрд▓реЛ рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдореЗрд░рд╛ рдЙрдкрд░реЛрдХреНрдд рд╕рдорд╛рдзрд╛рди рдЕрдм рдпреВрдЖрдИ рдореЗрдВ рдЕрдкреЛрд▓реЛ-рд╣реБрдХ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЧреНрд░рд╛рдлрдХреНрдпреВрдПрд▓ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рд╡рд╣рди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред

рдХреНрдпрд╛ рдХрд┐рд╕реА рдХреЗ рдкрд╛рд╕ рджреВрд╕рд░рд╛ рдЙрдкрд╛рдп рд╣реИ?

@strass рдХреНрдпрд╛ рдЖрдк рдмрддрд╛ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдКрдкрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдЖрдкрдХрд╛ рд╕рдорд╛рдзрд╛рди рд╕рдорд╕реНрдпрд╛ рдХреЛ рдареАрдХ рдХреНрдпреЛрдВ рдХрд░рддрд╛ рд╣реИред рдЖрдИрдПрдордУ рд╡реЗ forEach рдФрд░ map рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдмрд┐рд▓реНрдХреБрд▓ рд╡рд╣реА рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВред

рдпрд╣ рдЕрдм рдФрд░ рдирд╣реАрдВ рд╣реИ (рджреЗрдЦреЗрдВ https://github.com/apollographql/apolo-link/issues/855#issuecomment-538010335 )

рдЗрд╕ рдкрд░ рдХреЛрдИ рдкреНрд░рдЧрддрд┐?

рдпрджрд┐ рд╣рдо рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╡рд╕реНрддреБ рдмрд┐рд▓реНрдХреБрд▓ рдирд╣реАрдВ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рдРрд╕реЗ рдорд╛рдорд▓реЗ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреЗ рд▓рд┐рдП рдХреИрд╕реЗ рд╣реИрдВ рдЬрд╣рд╛рдВ рд╕рд░реНрд╡рд░ "рд╕реНрдерд╛рди" рд╢реАрд░реНрд╖рд▓реЗрдЦ рдХреЗ рд╕рд╛рде 302 рд▓реМрдЯрд╛ рд░рд╣рд╛ рд╣реИ?

рдЕрдЧрд░ рдХрд┐рд╕реА рдХреЗ рдкрд╛рд╕ рдХреЛрдИ рд╕реБрд░рд╛рдЧ рд╣реИ рддреЛ рдореБрдЭреЗ рдмрддрд╛рдПрдВ рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рдЕрдкреЛрд▓реЛ рдЫреЛрдбрд╝рдиреЗ рдХреА рд╕реЛрдЪ рд░рд╣реЗ рд╣реИрдВ :(

рдЗрд╕рд╕реЗ рдореБрдЭреЗ рд╡реНрдпрд╛рдкрдХ рд░реВрдк рд╕реЗ https://github.com/apollographql/apolo-link/issues/297#issuecomment -350488527 рдореЗрдВ рдорджрдж рдорд┐рд▓реА рдФрд░ рдиреЗрдЯрд╡рд░реНрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рдкрдврд╝рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдорд┐рд▓реАред

рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдкрд░рддреЛрдВ/рдЧреЗрдЯрд╡реЗ рдПрдкреАрдЖрдИ рд╕реЗ рдЕрдирдзрд┐рдХреГрдд рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдПрдВ рд▓реМрдЯрд╛рдП рдЬрд╛рдиреЗ рдкрд░ рдХреБрдЫ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИред

рдореБрдЭреЗ рдЗрд╕реЗ рдореБрдЦреНрдп рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЗ рд╣рд┐рд╕реНрд╕реЗ рдХреЗ рд░реВрдк рдореЗрдВ рджреЗрдЦрдирд╛ рдЕрдЪреНрдЫрд╛ рд▓рдЧреЗрдЧрд╛!

рдЗрд╕ рдкрд░ рдХреЛрдИ рдкреНрд░рдЧрддрд┐? рдРрд╕рд╛ рдХреЛрдИ рддрд░реАрдХрд╛ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдпрд╣ рдЗрдЪреНрдЫрд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░ рд╣реИред рдЧреИрд░-200 рд╕реНрдЯреЗрдЯрд╕ рдХреЛрдб рдХреЗ рд╕рд╛рде рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдЧреНрд░рд╛рдлрдХреНрдпреВрдПрд▓ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдФрд░ рдХреИрд╕реЗ рдЕрдЧреНрд░реЗрд╖рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ?

рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдкрдХрдбрд╝рдиреЗ рдФрд░ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд╕рд╛рде рд╕рдорд╕реНрдпрд╛ рдореЗрдВ рдЪрд▓ рд░рд╣реЗ рдХрд┐рд╕реА рдФрд░ рдХреЗ рд▓рд┐рдП рдмрд╕ рдПрдХ рдиреЛрдЯред рдореИрдВ рдЧреНрд░рд╛рдлрд┐рдХрд▓ рд╕реЗ рдЖрдВрддрд░рд┐рдХ рддреНрд░реБрдЯрд┐ рдХреЗ рд▓рд┐рдП рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдЦреЛрд▓рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рдерд╛ рдФрд░ рдпрд╣рд╛рдВ рдореИрдВ рдЗрд╕рдХреЗ рд╕рд╛рде рд╕рдорд╛рдкреНрдд рд╣реБрдЖ:

const errorHandler = onError(({ graphQLErrors, networkError, operation, forward, response }) => {
    if (graphQLErrors) {
      console.error('apollo errors', graphQLErrors);
    }
    if (networkError) {
      console.error('apollo network errors', networkError);
      if (!!networkError['error'] && !!networkError['error']['errors'] && networkError['error']['errors'][0]) {
        console.error('unwrapping apollo network errors');
        networkError.message = networkError['error']['errors'][0].message;
        // you may also be able to set networkError.message to null based on criteria to remove the error, even if you can't prevent an error from being triggered altogether
      }
    }
  }
);

// Create an http link:
const httpLink = new HttpLink(this.httpClient).create({
  uri: this.uri,
  headers: new HttpHeaders({
    Authorization: this.token
  }),
  includeExtensions: true
});

const httpErrorLink = ApolloLink.from([
  errorHandler,
  httpLink,
]);

рдЕрдирд┐рд╡рд╛рд░реНрдп рд░реВрдк рд╕реЗ рдореИрдВ рдЙрд╕ рд╕рдВрджреЗрд╢ рдХреЛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд░рддрд╛ рд╣реВрдВ рдЬреЛ рдкрд╣рд▓реЗ рдЖрдВрддрд░рд┐рдХ рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рд╣реИред рдореЗрд░реЗ рдЬреИрд╕реЗ рдХреБрдЫ рд▓реЛрдЧреЛрдВ рдХреА рдорджрдж рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдпрд╣рд╛рдВ рд╕рдорд╛рдкреНрдд рд╣реЛ рдЧрдПред

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

sandersn picture sandersn  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

vjpr picture vjpr  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

j3ddesign picture j3ddesign  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

bwhitty picture bwhitty  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

MoonTahoe picture MoonTahoe  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ