1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.graphql.command;
23
24 import org.opencastproject.authorization.xacml.manager.api.AclService;
25 import org.opencastproject.authorization.xacml.manager.api.AclServiceFactory;
26 import org.opencastproject.elasticsearch.api.SearchIndexException;
27 import org.opencastproject.elasticsearch.index.ElasticsearchIndex;
28 import org.opencastproject.graphql.event.GqlEvent;
29 import org.opencastproject.graphql.exception.GraphQLNotFoundException;
30 import org.opencastproject.graphql.exception.GraphQLRuntimeException;
31 import org.opencastproject.graphql.exception.GraphQLUnauthorizedException;
32 import org.opencastproject.graphql.execution.context.OpencastContext;
33 import org.opencastproject.graphql.execution.context.OpencastContextManager;
34 import org.opencastproject.graphql.type.input.AccessControlListInput;
35 import org.opencastproject.graphql.type.input.GqlCommonEventMetadataInput;
36 import org.opencastproject.graphql.util.GraphQLObjectMapper;
37 import org.opencastproject.graphql.util.MetadataFieldToGraphQLConverter;
38 import org.opencastproject.index.service.api.IndexService;
39 import org.opencastproject.index.service.catalog.adapter.events.CommonEventCatalogUIAdapter;
40 import org.opencastproject.index.service.exception.IndexServiceException;
41 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
42 import org.opencastproject.metadata.dublincore.DublinCoreMetadataCollection;
43 import org.opencastproject.metadata.dublincore.MetadataField;
44 import org.opencastproject.metadata.dublincore.MetadataList;
45 import org.opencastproject.security.api.AccessControlEntry;
46 import org.opencastproject.security.api.AccessControlList;
47 import org.opencastproject.security.api.SecurityService;
48 import org.opencastproject.security.api.UnauthorizedException;
49 import org.opencastproject.util.NotFoundException;
50
51
52 import java.time.Duration;
53 import java.time.OffsetDateTime;
54 import java.time.format.DateTimeFormatter;
55 import java.util.Map;
56
57 import graphql.schema.GraphQLScalarType;
58 import graphql.schema.GraphQLTypeUtil;
59
60 public class UpdateEventCommand extends AbstractCommand<GqlEvent> {
61
62 private final String eventId;
63
64 public UpdateEventCommand(final Builder builder) {
65 super(builder);
66 this.eventId = builder.eventId;
67 }
68
69 @Override
70 public GqlEvent execute() {
71 OpencastContext context = OpencastContextManager.getCurrentContext();
72 final ElasticsearchIndex index = context.getService(ElasticsearchIndex.class);
73 final IndexService indexService = context.getService(IndexService.class);
74
75 final Map<String, Object> eventMetadata = environment.getArgument("metadata");
76 try {
77 indexService.updateEventMetadata(eventId, createMetadataList(eventMetadata, indexService), index);
78 } catch (IndexServiceException | SearchIndexException e) {
79 throw new GraphQLRuntimeException(e);
80 } catch (UnauthorizedException e) {
81 throw new GraphQLUnauthorizedException(e.getMessage());
82 } catch (NotFoundException e) {
83 throw new GraphQLNotFoundException(e.getMessage());
84 }
85
86 final AccessControlListInput aclInput = GraphQLObjectMapper.newInstance()
87 .convertValue(environment.getArgument("acl"), AccessControlListInput.class);
88 if (aclInput != null) {
89 try {
90 AccessControlList acl = new AccessControlList();
91 for (var entry : aclInput.getEntries()) {
92 for (var action : entry.getAction()) {
93 acl.getEntries().add(new AccessControlEntry(entry.getRole(), action, true));
94 }
95 }
96
97 if (aclInput.getManagedAclId() != null) {
98 AclService aclService = context.getService(AclServiceFactory.class)
99 .serviceFor(context.getService(SecurityService.class).getOrganization());
100 aclService.getAcl(aclInput.getManagedAclId())
101 .ifPresent(value -> acl.merge(value.getAcl()));
102 }
103 indexService.updateEventAcl(eventId, acl, index);
104 } catch (IndexServiceException | SearchIndexException e) {
105 throw new GraphQLRuntimeException(e);
106 } catch (UnauthorizedException e) {
107 throw new GraphQLUnauthorizedException(e.getMessage());
108 } catch (NotFoundException e) {
109 throw new GraphQLNotFoundException(e.getMessage());
110 }
111 }
112
113 try {
114 return new GqlEvent(indexService.getEvent(eventId, index).get());
115 } catch (SearchIndexException e) {
116 throw new GraphQLRuntimeException(e);
117 }
118 }
119
120 private MetadataList createMetadataList(
121 Map<String, Object> eventMetadata,
122 IndexService indexService
123 ) {
124 CommonEventCatalogUIAdapter adapter = (CommonEventCatalogUIAdapter) indexService
125 .getCommonEventCatalogUIAdapter();
126
127 MetadataList list = new MetadataList();
128 list.add(adapter, adapter.getRawFields());
129
130 final MediaPackageElementFlavor flavor = MediaPackageElementFlavor
131 .parseFlavor("dublincore/episode");
132
133 final DublinCoreMetadataCollection collection = list.getMetadataByFlavor(flavor.toString());
134
135 for (Map.Entry<String, Object> entry: eventMetadata.entrySet()) {
136 String key = entry.getKey();
137 final MetadataField target = collection.getOutputFields().get(key);
138 var type = GraphQLTypeUtil.unwrapNonNull(MetadataFieldToGraphQLConverter.convertType(target));
139
140 Object value;
141 if (type instanceof GraphQLScalarType) {
142 value = ((GraphQLScalarType)type).getCoercing()
143 .parseValue(eventMetadata.get(key), environment.getGraphQlContext(), environment.getLocale());
144 } else {
145 value = eventMetadata.get(key);
146 }
147
148 if (value == null) {
149 continue;
150 }
151
152 switch (target.getType()) {
153 case DATE:
154 case START_DATE:
155 target.setValue(DateTimeFormatter.ofPattern(target.getPattern()).format((OffsetDateTime)value));
156 break;
157 case LONG:
158 target.setValue(value);
159 break;
160 case TEXT:
161 target.setValue(value);
162 break;
163 case BOOLEAN:
164 target.setValue(value);
165 break;
166 case DURATION:
167 target.setValue(((Duration) value).toMillis());
168 break;
169 case TEXT_LONG:
170 target.setValue(value);
171 break;
172 case MIXED_TEXT:
173 target.setValue(value);
174 break;
175 case START_TIME:
176 target.setValue(value);
177 break;
178 case ORDERED_TEXT:
179 target.setValue(value);
180 break;
181 case ITERABLE_TEXT:
182 target.setValue(value);
183 break;
184 default:
185 target.setValue(value);
186 break;
187 }
188 }
189
190 return list;
191 }
192
193 public static Builder create(String eventId, GqlCommonEventMetadataInput eventMetadataInput) {
194 return new Builder(eventId, eventMetadataInput);
195 }
196
197 public static class Builder extends AbstractCommand.Builder<GqlEvent> {
198
199 private final String eventId;
200
201 private final GqlCommonEventMetadataInput eventMetadataInput;
202
203 public Builder(String eventId, GqlCommonEventMetadataInput eventMetadataInput) {
204 this.eventId = eventId;
205 this.eventMetadataInput = eventMetadataInput;
206 }
207
208 @Override
209 public void validate() {
210 super.validate();
211 if (eventId == null || eventId.isEmpty()) {
212 throw new IllegalStateException("Event ID cannot be null or empty");
213 }
214 if (eventMetadataInput == null) {
215 throw new IllegalStateException("Event metadata cannot be null");
216 }
217 }
218
219 @Override
220 public UpdateEventCommand build() {
221 validate();
222 return new UpdateEventCommand(this);
223 }
224 }
225
226 }