001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2024-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.web;
019
020import com.google.protobuf.Descriptors.DescriptorValidationException;
021import com.google.protobuf.ExtensionRegistryLite;
022import com.google.protobuf.InvalidProtocolBufferException;
023
024import dev.enola.common.protobuf.TypeRegistryWrapper;
025import dev.enola.core.proto.EnolaServiceGrpc.EnolaServiceBlockingStub;
026import dev.enola.core.proto.GetFileDescriptorSetRequest;
027import dev.enola.core.proto.GetThingRequest;
028import dev.enola.core.thing.ListThingService;
029import dev.enola.core.view.EnolaMessages;
030import dev.enola.data.Repository;
031import dev.enola.thing.message.MessageToThingConverter;
032import dev.enola.thing.message.MessageWithIRI;
033import dev.enola.thing.message.MoreThings;
034import dev.enola.thing.proto.Thing;
035
036import java.io.IOException;
037import java.io.UncheckedIOException;
038import java.util.ArrayList;
039
040public class EnolaThingProvider implements Repository<Thing> {
041
042    // TODO Replace everywhere with EnolaServiceProtoThingsProvider
043    // TODO implements ProtoThingRepository, ProtoThingProvider
044    // TODO Move into dev.enola.core.thing where it probably belongs, more logically?
045    // TODO Resolve (some) overlap this class has with abstract class ProtoToThingConnector
046    // TODO Resolve (some) overlap this class has with abstract class ThingConnectorsProvider
047
048    private final MessageToThingConverter m2t = new MessageToThingConverter();
049    private final EnolaServiceBlockingStub service;
050    private final EnolaMessages enolaMessages;
051
052    public EnolaThingProvider(EnolaServiceBlockingStub service)
053            throws DescriptorValidationException {
054        this.service = service;
055
056        var gfdsr = GetFileDescriptorSetRequest.newBuilder().build();
057        var fds = service.getFileDescriptorSet(gfdsr).getProtos();
058        TypeRegistryWrapper typeRegistryWrapper = TypeRegistryWrapper.from(fds);
059        var extensionRegistry = ExtensionRegistryLite.getEmptyRegistry();
060        enolaMessages = new EnolaMessages(typeRegistryWrapper, extensionRegistry);
061    }
062
063    @Override
064    public Thing get(String iri) {
065        var request = GetThingRequest.newBuilder().setIri(iri).build();
066        var response = service.getThing(request);
067
068        if (!response.hasThing()) {
069            return null;
070        }
071        var any = response.getThing();
072
073        try {
074            var things = MoreThings.fromAny(any);
075            if (!things.isEmpty()) {
076                // TODO This is wrong, we need to return Things here, and UI needs to show all?
077                return things.getFirst();
078            } else {
079                var message = enolaMessages.toMessage(any);
080                return m2t.convert(new MessageWithIRI(iri, message)).build();
081            }
082        } catch (IOException e) {
083            throw new UncheckedIOException(e);
084        }
085    }
086
087    @Override
088    public Iterable<Thing> list() {
089        var iri = ListThingService.ENOLA_ROOT_LIST_THINGS;
090        var request = GetThingRequest.newBuilder().setIri(iri).build();
091        var response = service.getThing(request);
092        if (!response.hasThing()) throw new IllegalArgumentException();
093        var any = response.getThing();
094        try {
095            return MoreThings.fromAny(any);
096        } catch (InvalidProtocolBufferException e) {
097            throw new UncheckedIOException(e);
098        }
099    }
100
101    @Override
102    public Iterable<String> listIRI() {
103        var listProtoThing = get(ListThingService.ENOLA_ROOT_LIST_IRIS);
104        var valueList =
105                listProtoThing
106                        .getPropertiesMap()
107                        .get(ListThingService.ENOLA_ROOT_LIST_PROPERTY)
108                        .getList()
109                        .getValuesList();
110        var iriList = new ArrayList<String>(valueList.size());
111        for (var value : valueList) {
112            if (value.hasLink()) {
113                iriList.add(value.getLink());
114            }
115        }
116        return iriList;
117    }
118}