Coverage for gws-app/gws/base/ows/client/operation.py: 98%

62 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-23 20:06 +0200

1"""OWS operations of remote services.""" 

2 

3from typing import Optional 

4 

5import gws 

6import gws.lib.mime 

7 

8 

9class Config(gws.Config): 

10 """OWS operation that overrides or adds to those in the service capabilities.""" 

11 

12 verb: gws.OwsVerb 

13 """OWS request type.""" 

14 formats: Optional[list[str]] 

15 """MIME types supported by the operation.""" 

16 params: Optional[dict] 

17 """Extra parameters added to GET requests for this operation.""" 

18 postUrl: Optional[gws.Url] 

19 """URL for POST requests.""" 

20 url: Optional[gws.Url] 

21 """URL for GET requests.""" 

22 

23 

24def create( 

25 verb: gws.OwsVerb, 

26 op_from_caps: Optional[gws.OwsOperation] = None, 

27 cfgs: Optional[list[Config]] = None, 

28) -> gws.OwsOperation: 

29 """Create an operation from the capabilities and the configuration. 

30 

31 All fields are initialized. Attributes of the operations with the same verb in the 

32 configuration replace those of ``op_from_caps``. The default MIME type is the first 

33 configured one, if ``formats`` are configured, otherwise selected from the supported ones. 

34 

35 Args: 

36 verb: Request type. 

37 op_from_caps: Operation read from the capabilities, with MIME types as spelled by the service. 

38 cfgs: Operation configurations. 

39 

40 Returns: 

41 The operation. 

42 """ 

43 

44 co = op_from_caps or gws.OwsOperation() 

45 op = gws.OwsOperation( 

46 allowedParameters=co.allowedParameters or {}, 

47 constraints=co.constraints or {}, 

48 mimeTypeMap=dict(co.mimeTypeMap or {}), 

49 mimeTypes=list(co.mimeTypes or []), 

50 params=co.params or {}, 

51 postUrl=co.postUrl or '', 

52 url=co.url or '', 

53 verb=verb, 

54 ) 

55 

56 if op.mimeTypes: 

57 _set_mime_types(op, op.mimeTypes) 

58 

59 has_formats_config = False 

60 

61 if cfgs: 

62 for c in cfgs: 

63 if c.get('verb') != verb: 

64 continue 

65 p = c.get('params') 

66 if p is not None: 

67 op.params = p 

68 p = c.get('postUrl') 

69 if p is not None: 

70 op.postUrl = p 

71 p = c.get('url') 

72 if p is not None: 

73 op.url = p 

74 p = c.get('formats') 

75 if p is not None: 

76 _set_mime_types(op, p) 

77 has_formats_config = True 

78 

79 if has_formats_config and op.mimeTypes: 

80 op.defaultMimeType = op.mimeTypes[0] 

81 else: 

82 op.defaultMimeType = _default_mime_type(op) 

83 

84 return op 

85 

86 

87def _set_mime_types(op: gws.OwsOperation, values: list[str]): 

88 """Set the MIME types of an operation from the values a service offers. 

89 

90 ``mimeTypes`` gets the values normalized with ``gws.lib.mime.get`` (aliases resolved), ``mimeTypeMap`` 

91 maps those spelled differently by the service to the service spelling. Unknown MIME types are ignored. 

92 

93 Args: 

94 op: Operation. 

95 values: MIME types as spelled by the service. 

96 """ 

97 

98 op.mimeTypes = [] 

99 op.mimeTypeMap = {} 

100 for s in values: 

101 mime = gws.lib.mime.get(s) 

102 if not mime: 

103 gws.log.warning(f'{op.verb}: unknown MIME type {s!r} ignored') 

104 continue 

105 if mime in op.mimeTypes: 

106 continue 

107 op.mimeTypes.append(mime) 

108 if s != mime: 

109 op.mimeTypeMap[mime] = s 

110 

111 

112_PREFERRED_MIME_TYPES = { 

113 'image': [ 

114 gws.lib.mime.PNG8, 

115 gws.lib.mime.PNG24, 

116 gws.lib.mime.PNG, 

117 gws.lib.mime.JPEG, 

118 ], 

119 'text': [ 

120 gws.lib.mime.GML3, 

121 gws.lib.mime.GML32, 

122 gws.lib.mime.GML2, 

123 gws.lib.mime.GML, 

124 gws.lib.mime.XML, 

125 ], 

126} 

127 

128_IMAGE_VERBS = { 

129 gws.OwsVerb.GetLegendGraphic, 

130 gws.OwsVerb.GetMap, 

131 gws.OwsVerb.GetTile, 

132} 

133 

134 

135def _default_mime_type(op: gws.OwsOperation) -> str: 

136 """Pick the default MIME type of an operation. 

137 

138 The first preferred MIME type the operation supports is selected. Otherwise, the first supported 

139 MIME type with the same base type as a preferred one (``image/png; mode=16bit`` for ``image/png``), 

140 otherwise the first supported one. Without supported MIME types, the first preferred base type is used. 

141 """ 

142 

143 best_mimes = _PREFERRED_MIME_TYPES['image' if op.verb in _IMAGE_VERBS else 'text'] 

144 

145 if not op.mimeTypes: 

146 return gws.lib.mime.get_base(best_mimes[0]) 

147 

148 for mime in best_mimes: 

149 if mime in op.mimeTypes: 

150 return mime 

151 

152 for mime in op.mimeTypes: 

153 if gws.lib.mime.get_base(mime) in best_mimes: 

154 return mime 

155 

156 return op.mimeTypes[0]