text:
package org.elasticsearch.index;

import com.fasterxml.jackson.core.JsonParseException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.index.Term;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.logging.ESLogMessage;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.logging.MockAppender;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.IndexingSlowLog.IndexingSlowLogMessage;
import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.engine.InternalEngineTests;
import org.elasticsearch.index.mapper.ParsedDocument;
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
import org.elasticsearch.index.mapper.Uid;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.test.ESTestCase;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.mockito.Mockito;

import java.io.IOException;
import java.io.UncheckedIOException;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.emptyOrNullString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;

public class IndexingSlowLogTests extends ESTestCase {
    static MockAppender appender;
    static Logger testLogger1 = LogManager.getLogger(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_PREFIX + ".index");

    @BeforeClass
    public static void init() throws IllegalAccessException {
        appender = new MockAppender("trace_appender");
        appender.start();
        Loggers.addAppender(testLogger1, appender);
    }

    @AfterClass
    public static void cleanup() {
        appender.stop();
        Loggers.removeAppender(testLogger1, appender);
    }

    public void testLevelPrecedence() {
        String uuid = UUIDs.randomBase64UUID();
        IndexMetadata metadata = createIndexMetadata("index-precedence", settings(uuid));
        IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY);
        IndexingSlowLog log = new IndexingSlowLog(settings);

        ParsedDocument doc = InternalEngineTests.createParsedDoc("1", null);
        Engine.Index index = new Engine.Index(new Term("_id", Uid.encodeId("doc_id")), randomNonNegativeLong(), doc);
        Engine.IndexResult result = Mockito.mock(Engine.IndexResult.class);//(0, 0, SequenceNumbers.UNASSIGNED_SEQ_NO, false);
        Mockito.when(result.getResultType()).thenReturn(Engine.Result.Type.SUCCESS);

        // For this test, when level is not breached, the level below should be used.
        {
            Mockito.when(result.getTook()).thenReturn(40L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertThat(appender.getLastEventAndReset().getLevel(), equalTo(Level.INFO));

            Mockito.when(result.getTook()).thenReturn(41L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertThat(appender.getLastEventAndReset().getLevel(), equalTo(Level.WARN));
        }

        {
            Mockito.when(result.getTook()).thenReturn(30L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertThat(appender.getLastEventAndReset().getLevel(), equalTo(Level.DEBUG));

            Mockito.when(result.getTook()).thenReturn(31L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertThat(appender.getLastEventAndReset().getLevel(), equalTo(Level.INFO));
        }

        {
            Mockito.when(result.getTook()).thenReturn(20L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertThat(appender.getLastEventAndReset().getLevel(), equalTo(Level.TRACE));

            Mockito.when(result.getTook()).thenReturn(21L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertThat(appender.getLastEventAndReset().getLevel(), equalTo(Level.DEBUG));
        }

        {
            Mockito.when(result.getTook()).thenReturn(10L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertNull(appender.getLastEventAndReset());

            Mockito.when(result.getTook()).thenReturn(11L);
            log.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertThat(appender.getLastEventAndReset().getLevel(), equalTo(Level.TRACE));
        }
    }

    public void testTwoLoggersDifferentLevel() {
        IndexSettings index1Settings = new IndexSettings(createIndexMetadata("index1", Settings.builder()
            .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .put(IndexMetadata.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_WARN_SETTING.getKey(), "40nanos")
            .build()),
            Settings.EMPTY);
        IndexingSlowLog log1 = new IndexingSlowLog(index1Settings);

        IndexSettings index2Settings = new IndexSettings(createIndexMetadata("index2", Settings.builder()
            .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .put(IndexMetadata.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_TRACE_SETTING.getKey(), "10nanos")
            .build()),
            Settings.EMPTY);
        IndexingSlowLog log2 = new IndexingSlowLog(index2Settings);

        ParsedDocument doc = InternalEngineTests.createParsedDoc("1", null);
        Engine.Index index = new Engine.Index(new Term("_id", Uid.encodeId("doc_id")), randomNonNegativeLong(), doc);
        Engine.IndexResult result = Mockito.mock(Engine.IndexResult.class);
        Mockito.when(result.getResultType()).thenReturn(Engine.Result.Type.SUCCESS);

        {
            Mockito.when(result.getTook()).thenReturn(11L);

            // threshold set on WARN(40nanos) where 11nanos does not breach, should not log
            log1.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertNull(appender.getLastEventAndReset());

            // threshold set on TRACE(10nanos) and 11nanos breaches it, should log
            log2.postIndex(ShardId.fromString("[index][123]"), index, result);
            assertNotNull(appender.getLastEventAndReset());
        }
    }

    public void testMultipleSlowLoggersUseSingleLog4jLogger() {
        LoggerContext context = (LoggerContext) LogManager.getContext(false);

        IndexSettings index1Settings = new IndexSettings(createIndexMetadata("index1", settings(UUIDs.randomBase64UUID())),
            Settings.EMPTY);
        IndexingSlowLog log1 = new IndexingSlowLog(index1Settings);

        int numberOfLoggersBefore = context.getLoggers().size();

        IndexSettings index2Settings = new IndexSettings(createIndexMetadata("index2", settings(UUIDs.randomBase64UUID())),
            Settings.EMPTY);
        IndexingSlowLog log2 = new IndexingSlowLog(index2Settings);
        context = (LoggerContext) LogManager.getContext(false);

        int numberOfLoggersAfter = context.getLoggers().size();
        assertThat(numberOfLoggersAfter, equalTo(numberOfLoggersBefore));
    }

    private IndexMetadata createIndexMetadata(String index, Settings build) {
        return newIndexMeta(index, build);
    }

    private Settings settings(String uuid) {
        return Settings.builder()
            .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .put(IndexMetadata.SETTING_INDEX_UUID, uuid)
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_TRACE_SETTING.getKey(), "10nanos")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_DEBUG_SETTING.getKey(), "20nanos")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_INFO_SETTING.getKey(), "30nanos")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_WARN_SETTING.getKey(), "40nanos")
            .build();
    }

    public void testSlowLogMessageHasJsonFields() throws IOException {
        BytesReference source = BytesReference.bytes(JsonXContent.contentBuilder()
            .startObject().field("foo", "bar").endObject());
        ParsedDocument pd = new ParsedDocument(new NumericDocValuesField("version", 1),
            SeqNoFieldMapper.SequenceIDFields.emptySeqID(), "id",
            "routingValue", null, source, XContentType.JSON, null);
        Index index = new Index("foo", "123");
        // Turning off document logging doesn't log source[]
        ESLogMessage p =  IndexingSlowLogMessage.of(index, pd, 10, true, 0);

        assertThat(p.get("message"),equalTo("[foo/123]"));
        assertThat(p.get("took"),equalTo("10nanos"));
        assertThat(p.get("took_millis"),equalTo("0"));
        assertThat(p.get("id"),equalTo("id"));
        assertThat(p.get("routing"),equalTo("routingValue"));
        assertThat(p.get("source"), is(emptyOrNullString()));

        // Turning on document logging logs the whole thing
        p =  IndexingSlowLogMessage.of(index, pd, 10, true, Integer.MAX_VALUE);
        assertThat(p.get("source"), containsString("{\\\"foo\\\":\\\"bar\\\"}"));
    }

    public void testEmptyRoutingField() throws IOException {
        BytesReference source = BytesReference.bytes(JsonXContent.contentBuilder()
                                                                 .startObject().field("foo", "bar").endObject());
        ParsedDocument pd = new ParsedDocument(new NumericDocValuesField("version", 1),
            SeqNoFieldMapper.SequenceIDFields.emptySeqID(), "id",
            null, null, source, XContentType.JSON, null);
        Index index = new Index("foo", "123");

        ESLogMessage p = IndexingSlowLogMessage.of(index, pd, 10, true, 0);
        assertThat(p.get("routing"), nullValue());
    }

    public void testSlowLogParsedDocumentPrinterSourceToLog() throws IOException {
        BytesReference source = BytesReference.bytes(JsonXContent.contentBuilder()
            .startObject().field("foo", "bar").endObject());
        ParsedDocument pd = new ParsedDocument(new NumericDocValuesField("version", 1),
            SeqNoFieldMapper.SequenceIDFields.emptySeqID(), "id", null, null, source, XContentType.JSON, null);
        Index index = new Index("foo", "123");
        // Turning off document logging doesn't log source[]
        ESLogMessage p = IndexingSlowLogMessage.of(index, pd, 10, true, 0);
        assertThat(p.getFormattedMessage(), not(containsString("source[")));

        // Turning on document logging logs the whole thing
        p = IndexingSlowLogMessage.of(index, pd, 10, true, Integer.MAX_VALUE);
        assertThat(p.get("source"), equalTo("{\\\"foo\\\":\\\"bar\\\"}"));

        // And you can truncate the source
        p = IndexingSlowLogMessage.of(index, pd, 10, true, 3);
        assertThat(p.get("source"), equalTo("{\\\"f"));

        // And you can truncate the source
        p = IndexingSlowLogMessage.of(index, pd, 10, true, 3);
        assertThat(p.get("source"), containsString("{\\\"f"));
        assertThat(p.get("message"), startsWith("[foo/123]"));
        assertThat(p.get("took"), containsString("10nanos"));

        // Throwing a error if source cannot be converted
        source = new BytesArray("invalid");
        ParsedDocument doc = new ParsedDocument(new NumericDocValuesField("version", 1),
            SeqNoFieldMapper.SequenceIDFields.emptySeqID(), "id", null, null, source, XContentType.JSON, null);

        final UncheckedIOException e = expectThrows(UncheckedIOException.class,
            () -> IndexingSlowLogMessage.of(index, doc, 10, true, 3));
        assertThat(e, hasToString(containsString("_failed_to_convert_[Unrecognized token 'invalid':"
            + " was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\\n"
            + " at [Source: (org.elasticsearch.common.bytes.AbstractBytesReference$MarkSupportingStreamInputWrapper)")));
        assertNotNull(e.getCause());
        assertThat(e.getCause(), instanceOf(JsonParseException.class));
        assertThat(e.getCause(), hasToString(containsString("Unrecognized token 'invalid':"
                + " was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\n"
                + " at [Source: (org.elasticsearch.common.bytes.AbstractBytesReference$MarkSupportingStreamInputWrapper)")));
    }

    public void testReformatSetting() {
        IndexMetadata metadata = newIndexMeta("index", Settings.builder()
            .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_REFORMAT_SETTING.getKey(), false)
            .build());
        IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY);
        IndexingSlowLog log = new IndexingSlowLog(settings);
        assertFalse(log.isReformat());
        settings.updateIndexMetadata(newIndexMeta("index",
            Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_REFORMAT_SETTING.getKey(), "true").build()));
        assertTrue(log.isReformat());

        settings.updateIndexMetadata(newIndexMeta("index",
            Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_REFORMAT_SETTING.getKey(), "false").build()));
        assertFalse(log.isReformat());

        settings.updateIndexMetadata(newIndexMeta("index", Settings.EMPTY));
        assertTrue(log.isReformat());

        metadata = newIndexMeta("index", Settings.builder()
            .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .build());
        settings = new IndexSettings(metadata, Settings.EMPTY);
        log = new IndexingSlowLog(settings);
        assertTrue(log.isReformat());
        try {
            settings.updateIndexMetadata(newIndexMeta("index",
                Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_REFORMAT_SETTING.getKey(), "NOT A BOOLEAN").build()));
            fail();
        } catch (IllegalArgumentException ex) {
            final String expected = "illegal value can't update [index.indexing.slowlog.reformat] from [true] to [NOT A BOOLEAN]";
            assertThat(ex, hasToString(containsString(expected)));
            assertNotNull(ex.getCause());
            assertThat(ex.getCause(), instanceOf(IllegalArgumentException.class));
            final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause();
            assertThat(cause,
                hasToString(containsString("Failed to parse value [NOT A BOOLEAN] as only [true] or [false] are allowed.")));
        }
        assertTrue(log.isReformat());
    }

    public void testSetLevels() {
        IndexMetadata metadata = newIndexMeta("index", Settings.builder()
            .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_TRACE_SETTING.getKey(), "100ms")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_DEBUG_SETTING.getKey(), "200ms")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_INFO_SETTING.getKey(), "300ms")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_WARN_SETTING.getKey(), "400ms")
            .build());
        IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY);
        IndexingSlowLog log = new IndexingSlowLog(settings);
        assertEquals(TimeValue.timeValueMillis(100).nanos(), log.getIndexTraceThreshold());
        assertEquals(TimeValue.timeValueMillis(200).nanos(), log.getIndexDebugThreshold());
        assertEquals(TimeValue.timeValueMillis(300).nanos(), log.getIndexInfoThreshold());
        assertEquals(TimeValue.timeValueMillis(400).nanos(), log.getIndexWarnThreshold());

        settings.updateIndexMetadata(newIndexMeta("index",
            Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_TRACE_SETTING.getKey(), "120ms")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_DEBUG_SETTING.getKey(), "220ms")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_INFO_SETTING.getKey(), "320ms")
            .put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_WARN_SETTING.getKey(), "420ms").build()));

        assertEquals(TimeValue.timeValueMillis(120).nanos(), log.getIndexTraceThreshold());
        assertEquals(TimeValue.timeValueMillis(220).nanos(), log.getIndexDebugThreshold());
        assertEquals(TimeValue.timeValueMillis(320).nanos(), log.getIndexInfoThreshold());
        assertEquals(TimeValue.timeValueMillis(420).nanos(), log.getIndexWarnThreshold());

        metadata = newIndexMeta("index", Settings.builder()
            .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .build());
        settings.updateIndexMetadata(metadata);
        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexTraceThreshold());
        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexDebugThreshold());
        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexInfoThreshold());
        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexWarnThreshold());

        settings = new IndexSettings(metadata, Settings.EMPTY);
        log = new IndexingSlowLog(settings);

        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexTraceThreshold());
        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexDebugThreshold());
        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexInfoThreshold());
        assertEquals(TimeValue.timeValueMillis(-1).nanos(), log.getIndexWarnThreshold());
        try {
            settings.updateIndexMetadata(newIndexMeta("index",
                Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_TRACE_SETTING.getKey(), "NOT A TIME VALUE")
                    .build()));
            fail();
        } catch (IllegalArgumentException ex) {
            assertTimeValueException(ex, "index.indexing.slowlog.threshold.index.trace");
        }

        try {
            settings.updateIndexMetadata(newIndexMeta("index",
                Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_DEBUG_SETTING.getKey(), "NOT A TIME VALUE")
                    .build()));
            fail();
        } catch (IllegalArgumentException ex) {
            assertTimeValueException(ex, "index.indexing.slowlog.threshold.index.debug");
        }

        try {
            settings.updateIndexMetadata(newIndexMeta("index",
                Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_INFO_SETTING.getKey(), "NOT A TIME VALUE")
                    .build()));
            fail();
        } catch (IllegalArgumentException ex) {
            assertTimeValueException(ex, "index.indexing.slowlog.threshold.index.info");
        }

        try {
            settings.updateIndexMetadata(newIndexMeta("index",
                Settings.builder().put(IndexingSlowLog.INDEX_INDEXING_SLOWLOG_THRESHOLD_INDEX_WARN_SETTING.getKey(), "NOT A TIME VALUE")
                    .build()));
            fail();
        } catch (IllegalArgumentException ex) {
            assertTimeValueException(ex, "index.indexing.slowlog.threshold.index.warn");
        }
    }

    private void assertTimeValueException(final IllegalArgumentException e, final String key) {
        final String expected = "illegal value can't update [" + key + "] from [-1] to [NOT A TIME VALUE]";
        assertThat(e, hasToString(containsString(expected)));
        assertNotNull(e.getCause());
        assertThat(e.getCause(), instanceOf(IllegalArgumentException.class));
        final IllegalArgumentException cause = (IllegalArgumentException) e.getCause();
        final String causeExpected =
                "failed to parse setting [" + key + "] with value [NOT A TIME VALUE] as a time value: unit is missing or unrecognized";
        assertThat(cause, hasToString(containsString(causeExpected)));
    }

    private IndexMetadata newIndexMeta(String name, Settings indexSettings) {
        Settings build = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
            .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)
            .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
            .put(indexSettings)
            .build();
        IndexMetadata metadata = IndexMetadata.builder(name).settings(build).build();
        return metadata;
    }
}


text: package net.nativestruct.implementation.bytecode.constant;

redpajama_set_name: RedPajamaGithub


text:
title: Photos
layout: default
---

_coming soon_


text:
package com.netflix.zeno.diff.history;

import com.netflix.zeno.diff.DiffInstruction;
import com.netflix.zeno.diff.TypeDiffInstruction;
import com.netflix.zeno.fastblob.FastBlobStateEngine;
import com.netflix.zeno.util.SimultaneousExecutor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * A data structure to track the history of changes in a single FastBlobStateEngine.

 *
 * Each time the state engine consumes a blob file, call "addState()".  Then, at any time, pass in a key for an object identified in the
 * TypeDiffInstruction to retrieve a historical record of that data.

 *
 * This data structure retains a history of all changes in a data set across some specified number of rolling updates.  Because this retains a
 * large amount of data, it can consume a significant memory footprint, and resource availability should be planned accordingly.

 *
 * This class takes advantage of the guarantee that two identical objects across adjacent data states will be the same instance.  Comparisons
 * can therefore be done with ==, rather than checking for identical serialized representations.
 *
 * @author dkoszewnik
 *
 */
public class DiffHistoryTracker {

    private final int historySizeToKeep;
    private final FastBlobStateEngine stateEngine;
    private final LinkedList<DiffHistoricalState> historicalStates;
    private final Map<String, Map<String, String>> historicalStateHeaderTags;
    private final TypeDiffInstruction<?> typeDiffInstructions[];
    private DiffHistoryDataState currentDataState;

    /**
     *
     * @param numStatesToKeep - The number of historical states to keep
     * @param stateEngine - The state engine to track the history of
     * @param diffInstruction - The set of key extractions for types in the object model.
     */
    public DiffHistoryTracker(int numStatesToKeep, FastBlobStateEngine stateEngine, DiffInstruction diffInstruction) {
        this.historySizeToKeep = numStatesToKeep;
        this.stateEngine = stateEngine;
        this.historicalStates = new LinkedList<DiffHistoricalState>();
        this.historicalStateHeaderTags = new ConcurrentHashMap<String, Map<String,String>>();
        this.typeDiffInstructions = diffInstruction.getTypeInstructions();
    }

    /**
     * Call this method after new data has been loaded by the FastBlobStateEngine.  This will add a historical record
     * of the differences between the previous state and this new state.
     */
    public void addState() {
        DiffHistoryDataState nextState = new DiffHistoryDataState(stateEngine, typeDiffInstructions);

        if(currentDataState != null)
            newHistoricalState(currentDataState, nextState);

        currentDataState = nextState;
    }

    private void newHistoricalState(final DiffHistoryDataState from, final DiffHistoryDataState to) {
        final DiffHistoricalState historicalState = new DiffHistoricalState(to.getVersion());

        SimultaneousExecutor executor = new SimultaneousExecutor();

        for(final TypeDiffInstruction<?> typeInstruction : from.getTypeDiffInstructions()) {
            executor.execute(new Runnable() {
                public void run() {
                    Map<Object, Object> fromTypeState = from.getTypeState(typeInstruction.getTypeIdentifier());
                    Map<Object, Object> toTypeState = to.getTypeState(typeInstruction.getTypeIdentifier());

                    historicalState.addTypeState(typeInstruction, fromTypeState, toTypeState);
                }
            });
        }

        executor.awaitUninterruptibly();

        historicalStates.addFirst(historicalState);
        historicalStateHeaderTags.put(to.getVersion(), new HashMap<String, String>(stateEngine.getHeaderTags()));

        /// trim historical entries beyond desired size.
        if(historicalStates.size() > historySizeToKeep) {
            DiffHistoricalState removedState = historicalStates.removeLast();
            historicalStateHeaderTags.remove(removedState.getVersion());
        }
    }

    /**
     * Return the history of the object identified by the supplied type / key combination.

     *
     * The returned list will contain one entry for each state in the rolling history retained by this DiffHistoryTracker.
     * The latest entry will be at index 0, and the earliest entry will be the last in the list.  Not all entries in the returned
     * list must reflect a transition; an entry is included in the list whether or not the instance changed for a given state.
     *
     */
    public <T> List<DiffObjectHistoricalTransition<T>> getObjectHistory(String type, Object key) {
        List<DiffObjectHistoricalTransition<T>> states = new ArrayList<DiffObjectHistoricalTransition<T>>(historicalStates.size());
        Map<Object, T> typeState = currentDataState.getTypeState(type);

        /// start with the currently available item (if available)
        T currentItem = typeState.get(key);

        /// and work backwards through history.
        for(DiffHistoricalState state : historicalStates) {

            DiffHistoricalTypeState<Object, T> historicalState = state.getTypeState(type);

            Map<Object, T> diffObjects = historicalState.getDiffObjects();
            Map<Object, T> deletedObjects = historicalState.getDeletedObjects();
            Set<Object> newObjects = historicalState.getNewObjects();

            T previous;

            if(diffObjects.containsKey(key)) {
                previous = diffObjects.get(key);
            } else if(deletedObjects.containsKey(key)) {
                previous = deletedObjects.get(key);
            } else if(newObjects.contains(key)) {
                previous = null;
            } else {
                previous = currentItem;
            }

            /// adding the from -> to objects (whether identical or not) to a list.
            states.add(new DiffObjectHistoricalTransition<T>(state.getVersion(), previous, currentItem));
            currentItem = previous;
        }

        return states;
    }

    /**
     * Returns a list of the historical states, starting with the most recent and ending with the oldest.
     */
    public List<DiffHistoricalState> getHistoricalStates() {
        return Collections.unmodifiableList(historicalStates);
    }

    /**
     * Returns the header tags which were attached to the given version.
     */
    public Map<String, String> getHistoricalStateHeaderTags(String stateVersion) {
        return historicalStateHeaderTags.get(stateVersion);
    }

}


text:
#include <aws/inspector2/model/DisableDelegatedAdminAccountResult.h>
#include <aws/core/utils/json/JsonSerializer.h>
#include <aws/core/AmazonWebServiceResult.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/core/utils/UnreferencedParam.h>

#include <utility>

using namespace Aws::Inspector2::Model;
using namespace Aws::Utils::Json;
using namespace Aws::Utils;
using namespace Aws;

DisableDelegatedAdminAccountResult::DisableDelegatedAdminAccountResult()
{
}

DisableDelegatedAdminAccountResult::DisableDelegatedAdminAccountResult(const Aws::AmazonWebServiceResult<JsonValue>& result)
{
  *this = result;
}

DisableDelegatedAdminAccountResult& DisableDelegatedAdminAccountResult::operator =(const Aws::AmazonWebServiceResult<JsonValue>& result)
{
  JsonView jsonValue = result.GetPayload().View();
  if(jsonValue.ValueExists("delegatedAdminAccountId"))
  {
    m_delegatedAdminAccountId = jsonValue.GetString("delegatedAdminAccountId");

  }

  return *this;
}


text:
@implementation NTLNConfiguration

@synthesize useSafari, darkColorTheme, autoScroll, showMoreTweetMode, shakeToFullscreen;
@synthesize lefthand;

static id _instance = nil;

+ (id) instance {
    @synchronized (self) {
        if (!_instance) {
            _instance = [[self alloc] init];
        }
    }
    return _instance;
}

- (id) init {
	if (self = [super init]) {
		[self reload];
	}
	return self;
}

- (void)dealloc {
	[super dealloc];
}

- (void)reload {
	refreshIntervalSeconds = [[NSUserDefaults standardUserDefaults] integerForKey:NTLN_PREFERENCE_REFRESH_INTERVAL];
	useSafari = [[NSUserDefaults standardUserDefaults] boolForKey:NTLN_PREFERENCE_USE_SAFARI];
	darkColorTheme = [[NSUserDefaults standardUserDefaults] boolForKey:NTLN_PREFERENCE_DARK_COLOR_THEME];
	autoScroll = [[NSUserDefaults standardUserDefaults] boolForKey:NTLN_PREFERENCE_AUTO_SCROLL];
	showMoreTweetMode = [[NSUserDefaults standardUserDefaults] boolForKey:NTLN_PREFERENCE_SHOW_MORE_TWEETS_MODE];
	fetchCount = [[NSUserDefaults standardUserDefaults] integerForKey:NTLN_PREFERENCE_FETCH_COUNT];
	shakeToFullscreen = [[NSUserDefaults standardUserDefaults] boolForKey:NTLN_PREFERENCE_SHAKE_TO_FULLSCREEN];
	lefthand = [[NSUserDefaults standardUserDefaults] boolForKey:NTLN_PREFERENCE_LEFTHAND];
}

static int seconds_from_index(int index)
{
	if (index == 0) return 0;
	if (index > 6) return 0;

	const int m[] = {1*60,2*60,2*60+30,3*60,5*60,10*60};
	return m[index-1];
}

static int count_from_index(int index)
{
	if (index > 3) return 0;

	const int m[] = {20,50,100,200};
	return m[index];
}

- (int)refreshIntervalSeconds {
	return seconds_from_index(refreshIntervalSeconds);
}

- (int)fetchCount {
	return count_from_index(fetchCount);
}

@end


text:
layout: default
---
https://www.youtube.com/watch?v=ts6ErSJinaw


text:
Installation:

$ virtualenv flask
$ flask/bin/pip install -r requirements.txt


text:
package pl.allegro.tech.search.elasticsearch.tools.reindex.process;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.internal.InternalSearchHits;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.allegro.tech.search.elasticsearch.tools.reindex.statistics.ProcessStatistics;
import pl.allegro.tech.search.elasticsearch.tools.reindex.statistics.ReindexingSummary;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class ProcessSynchronizer {

  private static final Logger logger = LoggerFactory.getLogger(ProcessSynchronizer.class);

  private LinkedBlockingQueue<SearchHits> dataQueue;
  private CountDownLatch finishedQueringLatch;
  private CountDownLatch finishedUpdatesLatch;
  private ProcessStatistics statistics = new ProcessStatistics();
  private final int queuePollTimeout = ProcessConfiguration.getInstance().getQueuePollTimeout();
  private final int queueOfferTimeout = ProcessConfiguration.getInstance().getQueueOfferTimeout();
  private final List<Throwable> exceptions = Collections.synchronizedList(new LinkedList<Throwable>());

  public ProcessSynchronizer(int querySegmentCount) {
    this.dataQueue = new LinkedBlockingQueue(ProcessConfiguration.getInstance().getQueueSize());
    this.finishedQueringLatch = new CountDownLatch(querySegmentCount);
    this.finishedUpdatesLatch = new CountDownLatch(ProcessConfiguration.getInstance().getUpdateThreadsCount());
  }

  public void waitForProcessesToEnd() {
    try {
      finishedQueringLatch.await();
      finishedUpdatesLatch.await();
    } catch (InterruptedException e) {
      logger.error("Waiting for processes to end fails", e);
      Thread.currentThread().interrupt();
    }
  }

  public long getWorkingQueryProcessCount() {
    return finishedQueringLatch.getCount();
  }

  public void subtractWorkingQueryProcess() {
    finishedQueringLatch.countDown();
  }

  public void subtractWorkingUpdatesProcess() {
    finishedUpdatesLatch.countDown();
  }

  public void logStats() {
    exceptions.stream().forEach(
        exception -> logger.error("Processing Exception: ", exception)
    );
    statistics.log(dataQueue.size(), getWorkingQueryProcessCount());
  }

  public void incrementUpdates(int indexedCount) {
    statistics.incrementUpdates(indexedCount);
  }

  public void incrementQueries(int delta) {
    statistics.incrementQueries(delta);
  }

  public void incrementFailures(long count) {
    statistics.incrementFailures(count);
  }

  public boolean tryFillQueueWithSearchHits(SearchResponse response) {
    try {
      SearchHits hits = response.getHits();
      dataQueue.offer(hits, queueOfferTimeout, TimeUnit.MINUTES);
      incrementQueries(hits.getHits().length);
      logStats();
      return true;
    } catch (InterruptedException e) {
      logger.error("Fill Query Queue interrupted", e);
      Thread.currentThread().interrupt();
      return false;
    }
  }

  public SearchHits pollDataToIndexed() throws InterruptedException {
    SearchHits polled = dataQueue.poll(queuePollTimeout, TimeUnit.SECONDS);
    if (didTimeout(polled)) {
      return InternalSearchHits.empty();
    }
    return polled;
  }

  private boolean didTimeout(SearchHits polled) {
    return polled == null;
  }

  public boolean hasDataToBeIndexed() {
    return getWorkingQueryProcessCount() > 0 || dataQueue.size() > 0;
  }

  public ReindexingSummary getReindexingSummary() {
    return statistics.createReindexingSummary();
  }

  public void addProcessingException(Exception exception) {
    exceptions.add(exception);
  }
}


text:
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/crosapi/keystore_service_ash.h"

#include <stdint.h>

#include <string>
#include <utility>
#include <vector>

#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ash/attestation/tpm_challenge_key.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_service.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_factory.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
#include "chrome/browser/platform_keys/extension_platform_keys_service.h"
#include "chrome/browser/platform_keys/extension_platform_keys_service_factory.h"
#include "chrome/browser/platform_keys/platform_keys.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/crosapi/cpp/keystore_service_util.h"
#include "chromeos/crosapi/mojom/keystore_error.mojom.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom-shared.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "net/cert/x509_certificate.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/boringssl/src/include/openssl/pool.h"

namespace crosapi {

namespace {

using SigningAlgorithmName = mojom::KeystoreSigningAlgorithmName;
using SigningScheme = mojom::KeystoreSigningScheme;
using ::ash::platform_keys::KeyPermissionsService;
using ::ash::platform_keys::PlatformKeysService;
using ::chromeos::ExtensionPlatformKeysService;
using ::chromeos::platform_keys::TokenId;

const char kEnterprisePlatformErrorInvalidX509Cert[] =
    "Certificate is not a valid X.509 certificate.";
const char kUnsupportedKeystoreType[] = "The token is not valid.";
const char kUnsupportedAlgorithmType[] = "Algorithm type is not supported.";
const char kUnsupportedLacrosVersion[] = "Internal version incompatibility.";

// Converts a binary blob to a certificate.
scoped_refptr<net::X509Certificate> ParseCertificate(
    const std::vector<uint8_t>& input) {
  // Allow UTF-8 inside PrintableStrings in client certificates. See
  // crbug.com/770323 and crbug.com/788655.
  net::X509Certificate::UnsafeCreateOptions options;
  options.printable_string_is_utf8 = true;
  return net::X509Certificate::CreateFromBytesUnsafeOptions(input, options);
}

ExtensionPlatformKeysService* GetExtensionPlatformKeys() {
  ExtensionPlatformKeysService* service =
      chromeos::ExtensionPlatformKeysServiceFactory::GetForBrowserContext(
          ProfileManager::GetPrimaryUserProfile());
  CHECK(service);
  return service;
}

absl::optional<TokenId> KeystoreToToken(mojom::KeystoreType type) {
  if (!crosapi::mojom::IsKnownEnumValue(type)) {
    return absl::nullopt;
  }
  switch (type) {
    case mojom::KeystoreType::kUser:
      return TokenId::kUser;
    case mojom::KeystoreType::kDevice:
      return TokenId::kSystem;
  }
}

absl::optional<std::string> StringFromSigningAlgorithmName(
    SigningAlgorithmName name) {
  switch (name) {
    case SigningAlgorithmName::kRsassaPkcs115:
      return crosapi::keystore_service_util::kWebCryptoRsassaPkcs1v15;
    case SigningAlgorithmName::kEcdsa:
      return crosapi::keystore_service_util::kWebCryptoEcdsa;
    case SigningAlgorithmName::kUnknown:
      return absl::nullopt;
  }
}

bool UnpackSigningScheme(
    SigningScheme scheme,
    chromeos::platform_keys::KeyType* key_type,
    chromeos::platform_keys::HashAlgorithm* hash_algorithm) {
  using chromeos::platform_keys::HashAlgorithm;
  using chromeos::platform_keys::KeyType;

  switch (scheme) {
    case SigningScheme::kUnknown:
      return false;
    case SigningScheme::kRsassaPkcs1V15None:
      *key_type = KeyType::kRsassaPkcs1V15;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_NONE;
      return true;
    case SigningScheme::kRsassaPkcs1V15Sha1:
      *key_type = KeyType::kRsassaPkcs1V15;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA1;
      return true;
    case SigningScheme::kRsassaPkcs1V15Sha256:
      *key_type = KeyType::kRsassaPkcs1V15;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA256;
      return true;
    case SigningScheme::kRsassaPkcs1V15Sha384:
      *key_type = KeyType::kRsassaPkcs1V15;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA384;
      return true;
    case SigningScheme::kRsassaPkcs1V15Sha512:
      *key_type = KeyType::kRsassaPkcs1V15;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA512;
      return true;
    case SigningScheme::kEcdsaSha1:
      *key_type = KeyType::kEcdsa;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA1;
      return true;
    case SigningScheme::kEcdsaSha256:
      *key_type = KeyType::kEcdsa;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA256;
      return true;
    case SigningScheme::kEcdsaSha384:
      *key_type = KeyType::kEcdsa;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA384;
      return true;
    case SigningScheme::kEcdsaSha512:
      *key_type = KeyType::kEcdsa;
      *hash_algorithm = HashAlgorithm::HASH_ALGORITHM_SHA512;
      return true;
  }
  NOTREACHED();
  return false;
}

}  // namespace

KeystoreServiceAsh::KeystoreServiceAsh(content::BrowserContext* fixed_context)
    : fixed_platform_keys_service_(
          ash::platform_keys::PlatformKeysServiceFactory::GetForBrowserContext(
              fixed_context)),
      fixed_key_permissions_service_(
          ash::platform_keys::KeyPermissionsServiceFactory::
              GetForBrowserContext(fixed_context)) {
  CHECK(fixed_platform_keys_service_);
  CHECK(fixed_key_permissions_service_);
}

KeystoreServiceAsh::KeystoreServiceAsh(
    PlatformKeysService* platform_keys_service,
    KeyPermissionsService* key_permissions_service)
    : fixed_platform_keys_service_(platform_keys_service),
      fixed_key_permissions_service_(key_permissions_service) {
  CHECK(fixed_platform_keys_service_);
  CHECK(fixed_key_permissions_service_);
}

KeystoreServiceAsh::KeystoreServiceAsh() = default;
KeystoreServiceAsh::~KeystoreServiceAsh() = default;

void KeystoreServiceAsh::BindReceiver(
    mojo::PendingReceiver<mojom::KeystoreService> receiver) {
  receivers_.Add(this, std::move(receiver));
}

PlatformKeysService* KeystoreServiceAsh::GetPlatformKeys() {
  if (fixed_platform_keys_service_) {
    return fixed_platform_keys_service_;
  }

  PlatformKeysService* service =
      ash::platform_keys::PlatformKeysServiceFactory::GetForBrowserContext(
          ProfileManager::GetPrimaryUserProfile());
  CHECK(service);
  return service;
}

KeyPermissionsService* KeystoreServiceAsh::GetKeyPermissions() {
  if (fixed_key_permissions_service_) {
    return fixed_key_permissions_service_;
  }

  KeyPermissionsService* service =
      ash::platform_keys::KeyPermissionsServiceFactory::GetForBrowserContext(
          ProfileManager::GetPrimaryUserProfile());
  CHECK(service);
  return service;
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::ChallengeAttestationOnlyKeystore(
    mojom::KeystoreType type,
    const std::vector<uint8_t>& challenge,
    bool migrate,
    mojom::KeystoreSigningAlgorithmName algorithm,
    ChallengeAttestationOnlyKeystoreCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (!crosapi::mojom::IsKnownEnumValue(type)) {
    std::move(callback).Run(
        mojom::ChallengeAttestationOnlyKeystoreResult::NewErrorMessage(
            kUnsupportedKeystoreType));
    return;
  }

  attestation::KeyType key_crypto_type;
  switch (algorithm) {
    // Use RSA by default for backwards compatibility.
    case mojom::KeystoreSigningAlgorithmName::kUnknown:
    case mojom::KeystoreSigningAlgorithmName::kRsassaPkcs115:
      key_crypto_type = attestation::KEY_TYPE_RSA;
      break;
    case mojom::KeystoreSigningAlgorithmName::kEcdsa:
      key_crypto_type = attestation::KEY_TYPE_ECC;
      break;
  }

  ash::attestation::AttestationKeyType key_type;
  switch (type) {
    case mojom::KeystoreType::kUser:
      key_type = ash::attestation::KEY_USER;
      break;
    case mojom::KeystoreType::kDevice:
      key_type = ash::attestation::KEY_DEVICE;
      break;
  }
  Profile* profile = ProfileManager::GetActiveUserProfile();

  std::string key_name_for_spkac;
  if (migrate && (key_type == ash::attestation::KEY_DEVICE)) {
    key_name_for_spkac = base::StrCat(
        {ash::attestation::kEnterpriseMachineKeyForSpkacPrefix, "keystore-",
         base::UnguessableToken::Create().ToString()});
  }

  // The lifetime of this object is bound to the callback.
  std::unique_ptr<ash::attestation::TpmChallengeKey> challenge_key =
      ash::attestation::TpmChallengeKeyFactory::Create();
  ash::attestation::TpmChallengeKey* challenge_key_ptr = challenge_key.get();
  outstanding_challenges_.push_back(std::move(challenge_key));
  challenge_key_ptr->BuildResponse(
      key_type, profile,
      base::BindOnce(&KeystoreServiceAsh::DidChallengeAttestationOnlyKeystore,
                     weak_factory_.GetWeakPtr(), std::move(callback),
                     challenge_key_ptr),
      std::string(challenge.begin(), challenge.end()),
      /*register_key=*/migrate, key_crypto_type, key_name_for_spkac,
      /*signals=*/absl::nullopt);
}

void KeystoreServiceAsh::DidChallengeAttestationOnlyKeystore(
    ChallengeAttestationOnlyKeystoreCallback callback,
    void* challenge_key_ptr,
    const ash::attestation::TpmChallengeKeyResult& result) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  crosapi::mojom::ChallengeAttestationOnlyKeystoreResultPtr result_ptr;
  if (result.IsSuccess()) {
    result_ptr =
        mojom::ChallengeAttestationOnlyKeystoreResult::NewChallengeResponse(
            std::vector<uint8_t>(result.challenge_response.begin(),
                                 result.challenge_response.end()));
  } else {
    result_ptr = mojom::ChallengeAttestationOnlyKeystoreResult::NewErrorMessage(
        result.GetErrorMessage());
  }
  std::move(callback).Run(std::move(result_ptr));

  // Remove the outstanding challenge_key object.
  bool found = false;
  for (auto it = outstanding_challenges_.begin();
       it != outstanding_challenges_.end(); ++it) {
    if (it->get() == challenge_key_ptr) {
      outstanding_challenges_.erase(it);
      found = true;
      break;
    }
  }
  DCHECK(found);
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_ChallengeAttestationOnlyKeystore(
    const std::string& challenge,
    mojom::KeystoreType type,
    bool migrate,
    DEPRECATED_ChallengeAttestationOnlyKeystoreCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (!crosapi::mojom::IsKnownEnumValue(type)) {
    std::move(callback).Run(
        mojom::DEPRECATED_KeystoreStringResult::NewErrorMessage(
            kUnsupportedKeystoreType));
    return;
  }

  ash::attestation::AttestationKeyType key_type;
  switch (type) {
    case mojom::KeystoreType::kUser:
      key_type = ash::attestation::KEY_USER;
      break;
    case mojom::KeystoreType::kDevice:
      key_type = ash::attestation::KEY_DEVICE;
      break;
  }
  Profile* profile = ProfileManager::GetActiveUserProfile();

  std::string key_name_for_spkac;
  if (migrate && (key_type == ash::attestation::KEY_DEVICE)) {
    key_name_for_spkac =
        base::StrCat({ash::attestation::kEnterpriseMachineKeyForSpkacPrefix,
                      "lacros-", base::UnguessableToken::Create().ToString()});
  }

  std::unique_ptr<ash::attestation::TpmChallengeKey> challenge_key =
      ash::attestation::TpmChallengeKeyFactory::Create();
  ash::attestation::TpmChallengeKey* challenge_key_ptr = challenge_key.get();
  outstanding_challenges_.push_back(std::move(challenge_key));
  challenge_key_ptr->BuildResponse(
      key_type, profile,
      base::BindOnce(
          &KeystoreServiceAsh::DEPRECATED_DidChallengeAttestationOnlyKeystore,
          weak_factory_.GetWeakPtr(), std::move(callback), challenge_key_ptr),
      challenge,
      /*register_key=*/migrate, ::attestation::KEY_TYPE_RSA, key_name_for_spkac,
      /*signals=*/absl::nullopt);
}

void KeystoreServiceAsh::DEPRECATED_DidChallengeAttestationOnlyKeystore(
    DEPRECATED_ChallengeAttestationOnlyKeystoreCallback callback,
    void* challenge_key_ptr,
    const ash::attestation::TpmChallengeKeyResult& result) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  crosapi::mojom::DEPRECATED_KeystoreStringResultPtr result_ptr;
  if (result.IsSuccess()) {
    result_ptr = mojom::DEPRECATED_KeystoreStringResult::NewChallengeResponse(
        result.challenge_response);
  } else {
    result_ptr = mojom::DEPRECATED_KeystoreStringResult::NewErrorMessage(
        result.GetErrorMessage());
  }
  std::move(callback).Run(std::move(result_ptr));

  // Remove the outstanding challenge_key object.
  bool found = false;
  for (auto it = outstanding_challenges_.begin();
       it != outstanding_challenges_.end(); ++it) {
    if (it->get() == challenge_key_ptr) {
      outstanding_challenges_.erase(it);
      found = true;
      break;
    }
  }
  DCHECK(found);
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::GetKeyStores(GetKeyStoresCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  GetPlatformKeys()->GetTokens(base::BindOnce(
      &KeystoreServiceAsh::DidGetKeyStores, std::move(callback)));
}

// static
void KeystoreServiceAsh::DidGetKeyStores(
    GetKeyStoresCallback callback,
    std::unique_ptr<std::vector<TokenId>> platform_keys_token_ids,
    chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  mojom::GetKeyStoresResultPtr result_ptr;

  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::vector<mojom::KeystoreType> key_stores;
    for (auto token_id : *platform_keys_token_ids) {
      switch (token_id) {
        case TokenId::kUser:
          key_stores.push_back(mojom::KeystoreType::kUser);
          break;
        case TokenId::kSystem:
          key_stores.push_back(mojom::KeystoreType::kDevice);
          break;
      }
    }
    result_ptr = mojom::GetKeyStoresResult::NewKeyStores(std::move(key_stores));
  } else {
    result_ptr = mojom::GetKeyStoresResult::NewError(
        chromeos::platform_keys::StatusToKeystoreError(status));
  }

  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_GetKeyStores(
    DEPRECATED_GetKeyStoresCallback callback) {
  CHECK(false) << "DEPRECATED_GetKeyStores method was called.";
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::SelectClientCertificates(
    const std::vector<std::vector<uint8_t>>& certificate_authorities,
    SelectClientCertificatesCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  std::vector<std::string> cert_authorities_str;
  cert_authorities_str.reserve(certificate_authorities.size());
  for (const std::vector<uint8_t>& ca : certificate_authorities) {
    cert_authorities_str.emplace_back(ca.begin(), ca.end());
  }

  GetPlatformKeys()->SelectClientCertificates(
      std::move(cert_authorities_str),
      base::BindOnce(&KeystoreServiceAsh::DidSelectClientCertificates,
                     std::move(callback)));
}

// static
void KeystoreServiceAsh::DidSelectClientCertificates(
    SelectClientCertificatesCallback callback,
    std::unique_ptr<net::CertificateList> matches,
    chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  mojom::KeystoreSelectClientCertificatesResultPtr result_ptr;

  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::vector<std::vector<uint8_t>> output;
    for (scoped_refptr<net::X509Certificate> cert : *matches) {
      CRYPTO_BUFFER* der_buffer = cert->cert_buffer();
      const uint8_t* data = CRYPTO_BUFFER_data(der_buffer);
      std::vector<uint8_t> der_x509_certificate(
          data, data + CRYPTO_BUFFER_len(der_buffer));
      output.push_back(std::move(der_x509_certificate));
    }
    result_ptr = mojom::KeystoreSelectClientCertificatesResult::NewCertificates(
        std::move(output));
  } else {
    result_ptr = mojom::KeystoreSelectClientCertificatesResult::NewError(
        chromeos::platform_keys::StatusToKeystoreError(status));
  }

  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::GetCertificates(mojom::KeystoreType keystore,
                                         GetCertificatesCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  PlatformKeysService* platform_keys_service = GetPlatformKeys();
  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(mojom::GetCertificatesResult::NewError(
        mojom::KeystoreError::kUnsupportedKeystoreType));
    return;
  }

  platform_keys_service->GetCertificates(
      token_id.value(), base::BindOnce(&KeystoreServiceAsh::DidGetCertificates,
                                       std::move(callback)));
}

// static
void KeystoreServiceAsh::DidGetCertificates(
    GetCertificatesCallback callback,
    std::unique_ptr<net::CertificateList> certs,
    chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  mojom::GetCertificatesResultPtr result_ptr;

  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::vector<std::vector<uint8_t>> output;
    for (scoped_refptr<net::X509Certificate> cert : *certs) {
      CRYPTO_BUFFER* der_buffer = cert->cert_buffer();
      const uint8_t* data = CRYPTO_BUFFER_data(der_buffer);
      std::vector<uint8_t> der_x509_certificate(
          data, data + CRYPTO_BUFFER_len(der_buffer));
      output.push_back(std::move(der_x509_certificate));
    }
    result_ptr =
        mojom::GetCertificatesResult::NewCertificates(std::move(output));
  } else {
    result_ptr = mojom::GetCertificatesResult::NewError(
        chromeos::platform_keys::StatusToKeystoreError(status));
  }

  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_GetCertificates(
    mojom::KeystoreType keystore,
    DEPRECATED_GetCertificatesCallback callback) {
  CHECK(false) << "DEPRECATED_GetCertificates method was called.";
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::AddCertificate(mojom::KeystoreType keystore,
                                        const std::vector<uint8_t>& certificate,
                                        AddCertificateCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  scoped_refptr<net::X509Certificate> cert_x509 = ParseCertificate(certificate);
  if (!cert_x509.get()) {
    std::move(callback).Run(/*is_error=*/true,
                            mojom::KeystoreError::kCertificateInvalid);
    return;
  }
  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(/*is_error=*/true,
                            mojom::KeystoreError::kUnsupportedKeystoreType);
    return;
  }

  PlatformKeysService* platform_keys_service = GetPlatformKeys();
  platform_keys_service->ImportCertificate(
      token_id.value(), cert_x509,
      base::BindOnce(&KeystoreServiceAsh::DidImportCertificate,
                     std::move(callback)));
}

void KeystoreServiceAsh::DidImportCertificate(
    AddCertificateCallback callback,
    chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::move(callback).Run(/*is_error=*/false, mojom::KeystoreError::kUnknown);
  } else {
    std::move(callback).Run(
        /*is_error=*/true,
        chromeos::platform_keys::StatusToKeystoreError(status));
  }
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_AddCertificate(
    mojom::KeystoreType keystore,
    const std::vector<uint8_t>& certificate,
    DEPRECATED_AddCertificateCallback callback) {
  CHECK(false) << "DEPRECATED_AddCertificate method was called.";
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::RemoveCertificate(
    mojom::KeystoreType keystore,
    const std::vector<uint8_t>& certificate,
    RemoveCertificateCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  scoped_refptr<net::X509Certificate> cert_x509 = ParseCertificate(certificate);
  if (!cert_x509.get()) {
    std::move(callback).Run(/*is_error=*/true,
                            mojom::KeystoreError::kCertificateInvalid);
    return;
  }
  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(/*is_error=*/true,
                            mojom::KeystoreError::kUnsupportedKeystoreType);
    return;
  }

  PlatformKeysService* platform_keys_service = GetPlatformKeys();
  platform_keys_service->RemoveCertificate(
      token_id.value(), cert_x509,
      base::BindOnce(&KeystoreServiceAsh::DidRemoveCertificate,
                     std::move(callback)));
}

void KeystoreServiceAsh::DidRemoveCertificate(
    RemoveCertificateCallback callback,
    chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::move(callback).Run(/*is_error=*/false, mojom::KeystoreError::kUnknown);
  } else {
    std::move(callback).Run(
        /*is_error=*/true,
        chromeos::platform_keys::StatusToKeystoreError(status));
  }
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_RemoveCertificate(
    mojom::KeystoreType keystore,
    const std::vector<uint8_t>& certificate,
    DEPRECATED_RemoveCertificateCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  scoped_refptr<net::X509Certificate> cert_x509 = ParseCertificate(certificate);
  if (!cert_x509.get()) {
    std::move(callback).Run(kEnterprisePlatformErrorInvalidX509Cert);
    return;
  }
  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(kUnsupportedKeystoreType);
    return;
  }

  PlatformKeysService* platform_keys_service = GetPlatformKeys();
  platform_keys_service->RemoveCertificate(
      token_id.value(), cert_x509,
      base::BindOnce(&KeystoreServiceAsh::DEPRECATED_DidRemoveCertificate,
                     std::move(callback)));
}

void KeystoreServiceAsh::DEPRECATED_DidRemoveCertificate(
    DEPRECATED_RemoveCertificateCallback callback,
    chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (status == chromeos::platform_keys::Status::kSuccess)
    std::move(callback).Run(/*error=*/"");
  else
    std::move(callback).Run(chromeos::platform_keys::StatusToString(status));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::GetPublicKey(
    const std::vector<uint8_t>& certificate,
    mojom::KeystoreSigningAlgorithmName algorithm_name,
    GetPublicKeyCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  absl::optional<std::string> name =
      StringFromSigningAlgorithmName(algorithm_name);
  if (!name) {
    std::move(callback).Run(mojom::GetPublicKeyResult::NewError(
        mojom::KeystoreError::kAlgorithmNotPermittedByCertificate));
    return;
  }

  chromeos::platform_keys::GetPublicKeyAndAlgorithmOutput output =
      chromeos::platform_keys::GetPublicKeyAndAlgorithm(certificate,
                                                        name.value());

  mojom::GetPublicKeyResultPtr result_ptr;
  if (output.status == chromeos::platform_keys::Status::kSuccess) {
    absl::optional<crosapi::mojom::KeystoreSigningAlgorithmPtr>
        signing_algorithm =
            crosapi::keystore_service_util::SigningAlgorithmFromDictionary(
                output.algorithm);
    if (signing_algorithm) {
      mojom::GetPublicKeySuccessResultPtr success_result_ptr =
          mojom::GetPublicKeySuccessResult::New();
      success_result_ptr->public_key = std::move(output.public_key);
      success_result_ptr->algorithm_properties =
          std::move(signing_algorithm.value());
      result_ptr = mojom::GetPublicKeyResult::NewSuccessResult(
          std::move(success_result_ptr));
    } else {
      result_ptr = mojom::GetPublicKeyResult::NewError(
          crosapi::mojom::KeystoreError::kUnsupportedAlgorithmType);
    }
  } else {
    result_ptr = mojom::GetPublicKeyResult::NewError(
        chromeos::platform_keys::StatusToKeystoreError(output.status));
  }
  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_GetPublicKey(
    const std::vector<uint8_t>& certificate,
    mojom::KeystoreSigningAlgorithmName algorithm_name,
    DEPRECATED_GetPublicKeyCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  absl::optional<std::string> name =
      StringFromSigningAlgorithmName(algorithm_name);
  if (!name) {
    std::move(callback).Run(
        mojom::DEPRECATED_GetPublicKeyResult::NewErrorMessage(
            chromeos::platform_keys::StatusToString(
                chromeos::platform_keys::Status::
                    kErrorAlgorithmNotPermittedByCertificate)));
    return;
  }

  chromeos::platform_keys::GetPublicKeyAndAlgorithmOutput output =
      chromeos::platform_keys::GetPublicKeyAndAlgorithm(certificate,
                                                        name.value());

  mojom::DEPRECATED_GetPublicKeyResultPtr result_ptr;
  if (output.status == chromeos::platform_keys::Status::kSuccess) {
    absl::optional<crosapi::mojom::KeystoreSigningAlgorithmPtr>
        signing_algorithm =
            crosapi::keystore_service_util::SigningAlgorithmFromDictionary(
                output.algorithm);
    if (signing_algorithm) {
      mojom::GetPublicKeySuccessResultPtr success_result_ptr =
          mojom::GetPublicKeySuccessResult::New();
      success_result_ptr->public_key = std::move(output.public_key);
      success_result_ptr->algorithm_properties =
          std::move(signing_algorithm.value());
      result_ptr = mojom::DEPRECATED_GetPublicKeyResult::NewSuccessResult(
          std::move(success_result_ptr));
    } else {
      result_ptr = mojom::DEPRECATED_GetPublicKeyResult::NewErrorMessage(
          kUnsupportedAlgorithmType);
    }
  } else {
    result_ptr = mojom::DEPRECATED_GetPublicKeyResult::NewErrorMessage(
        chromeos::platform_keys::StatusToString(output.status));
  }
  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_ExtensionGenerateKey(
    mojom::KeystoreType keystore,
    mojom::KeystoreSigningAlgorithmPtr algorithm,
    const absl::optional<std::string>& extension_id,
    DEPRECATED_ExtensionGenerateKeyCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (!extension_id) {
    std::move(callback).Run(
        mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
            kUnsupportedLacrosVersion));
    return;
  }

  ExtensionPlatformKeysService* ext_platform_keys_service =
      GetExtensionPlatformKeys();
  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(
        mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
            kUnsupportedKeystoreType));
    return;
  }

  switch (algorithm->which()) {
    case mojom::KeystoreSigningAlgorithm::Tag::kPkcs115: {
      auto c = base::BindOnce(
          &KeystoreServiceAsh::DEPRECATED_DidExtensionGenerateKey,
          std::move(callback));
      ext_platform_keys_service->GenerateRSAKey(
          token_id.value(), algorithm->get_pkcs115()->modulus_length,
          algorithm->get_pkcs115()->sw_backed, *extension_id, std::move(c));
      break;
    }
    case mojom::KeystoreSigningAlgorithm::Tag::kEcdsa: {
      auto c = base::BindOnce(
          &KeystoreServiceAsh::DEPRECATED_DidExtensionGenerateKey,
          std::move(callback));
      ext_platform_keys_service->GenerateECKey(
          token_id.value(), algorithm->get_ecdsa()->named_curve, *extension_id,
          std::move(c));
      break;
    }
    default: {
      std::move(callback).Run(
          mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
              chromeos::platform_keys::StatusToString(
                  chromeos::platform_keys::Status::
                      kErrorAlgorithmNotSupported)));
      break;
    }
  }
}

// static
void KeystoreServiceAsh::DEPRECATED_DidExtensionGenerateKey(
    DEPRECATED_ExtensionGenerateKeyCallback callback,
    std::vector<uint8_t> public_key,
    absl::optional<crosapi::mojom::KeystoreError> error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  crosapi::mojom::DEPRECATED_ExtensionKeystoreBinaryResultPtr result_ptr;
  if (!error) {
    result_ptr = mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewBlob(
        std::move(public_key));
  } else {
    result_ptr =
        mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
            chromeos::platform_keys::KeystoreErrorToString(error.value()));
  }
  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::DEPRECATED_ExtensionSign(
    KeystoreType keystore,
    const std::vector<uint8_t>& public_key,
    SigningScheme scheme,
    const std::vector<uint8_t>& data,
    const std::string& extension_id,
    DEPRECATED_ExtensionSignCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(
        mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
            kUnsupportedKeystoreType));
    return;
  }

  ExtensionPlatformKeysService* service = GetExtensionPlatformKeys();
  chromeos::platform_keys::HashAlgorithm hash_algorithm;
  chromeos::platform_keys::KeyType key_type;
  switch (scheme) {
    case SigningScheme::kUnknown:
      std::move(callback).Run(
          mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
              kUnsupportedAlgorithmType));
      return;
    case SigningScheme::kRsassaPkcs1V15None:
      service->SignRSAPKCS1Raw(
          token_id, data, public_key, extension_id,
          base::BindOnce(&KeystoreServiceAsh::DEPRECATED_DidExtensionSign,
                         std::move(callback)));
      return;
    case SigningScheme::kRsassaPkcs1V15Sha1:
      key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1;
      break;
    case SigningScheme::kRsassaPkcs1V15Sha256:
      key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256;
      break;
    case SigningScheme::kRsassaPkcs1V15Sha384:
      key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384;
      break;
    case SigningScheme::kRsassaPkcs1V15Sha512:
      key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512;
      break;
    case SigningScheme::kEcdsaSha1:
      key_type = chromeos::platform_keys::KeyType::kEcdsa;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA1;
      break;
    case SigningScheme::kEcdsaSha256:
      key_type = chromeos::platform_keys::KeyType::kEcdsa;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA256;
      break;
    case SigningScheme::kEcdsaSha384:
      key_type = chromeos::platform_keys::KeyType::kEcdsa;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA384;
      break;
    case SigningScheme::kEcdsaSha512:
      key_type = chromeos::platform_keys::KeyType::kEcdsa;
      hash_algorithm = chromeos::platform_keys::HASH_ALGORITHM_SHA512;
      break;
  }

  service->SignDigest(
      token_id, data, public_key, key_type, hash_algorithm, extension_id,
      base::BindOnce(&KeystoreServiceAsh::DEPRECATED_DidExtensionSign,
                     std::move(callback)));
}

// static
void KeystoreServiceAsh::DEPRECATED_DidExtensionSign(
    DEPRECATED_ExtensionSignCallback callback,
    std::vector<uint8_t> signature,
    absl::optional<mojom::KeystoreError> error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  if (!error) {
    std::move(callback).Run(
        mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewBlob(
            std::move(signature)));
  } else {
    std::move(callback).Run(
        mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
            chromeos::platform_keys::KeystoreErrorToString(error.value())));
  }
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::GenerateKey(
    mojom::KeystoreType keystore,
    mojom::KeystoreSigningAlgorithmPtr algorithm,
    GenerateKeyCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  PlatformKeysService* platform_keys_service = GetPlatformKeys();
  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(mojom::KeystoreBinaryResult::NewError(
        mojom::KeystoreError::kUnsupportedKeystoreType));
    return;
  }

  using Tag = mojom::KeystoreSigningAlgorithm::Tag;
  switch (algorithm->which()) {
    case Tag::kPkcs115: {
      platform_keys_service->GenerateRSAKey(
          token_id.value(), algorithm->get_pkcs115()->modulus_length,
          algorithm->get_pkcs115()->sw_backed,
          base::BindOnce(&KeystoreServiceAsh::DidGenerateKey,
                         std::move(callback)));
      return;
    }
    case Tag::kEcdsa: {
      platform_keys_service->GenerateECKey(
          token_id.value(), algorithm->get_ecdsa()->named_curve,
          base::BindOnce(&KeystoreServiceAsh::DidGenerateKey,
                         std::move(callback)));
      return;
    }
    default: {
      std::move(callback).Run(mojom::KeystoreBinaryResult::NewError(
          mojom::KeystoreError::kAlgorithmNotSupported));
      return;
    }
  }
}

// static
void KeystoreServiceAsh::DidGenerateKey(
    GenerateKeyCallback callback,
    const std::string& public_key,
    chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  crosapi::mojom::KeystoreBinaryResultPtr result_ptr;
  if (status == chromeos::platform_keys::Status::kSuccess) {
    result_ptr = mojom::KeystoreBinaryResult::NewBlob(
        std::vector<uint8_t>(public_key.begin(), public_key.end()));
  } else {
    result_ptr = mojom::KeystoreBinaryResult::NewError(
        chromeos::platform_keys::StatusToKeystoreError(status));
  }
  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::RemoveKey(KeystoreType keystore,
                                   const std::vector<uint8_t>& public_key,
                                   RemoveKeyCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  absl::optional<TokenId> token_id = KeystoreToToken(keystore);
  if (!token_id) {
    std::move(callback).Run(/*is_error=*/true,
                            mojom::KeystoreError::kUnsupportedKeystoreType);
    return;
  }

  GetPlatformKeys()->RemoveKey(
      token_id.value(), std::string(public_key.begin(), public_key.end()),
      base::BindOnce(&KeystoreServiceAsh::DidRemoveKey, std::move(callback)));
}

// static
void KeystoreServiceAsh::DidRemoveKey(RemoveKeyCallback callback,
                                      chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::move(callback).Run(/*is_error=*/false, mojom::KeystoreError::kUnknown);
  } else {
    std::move(callback).Run(
        /*is_error=*/true,
        chromeos::platform_keys::StatusToKeystoreError(status));
  }
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::Sign(bool is_keystore_provided,
                              KeystoreType keystore,
                              const std::vector<uint8_t>& public_key,
                              SigningScheme scheme,
                              const std::vector<uint8_t>& data,
                              SignCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  absl::optional<TokenId> token_id;
  if (is_keystore_provided) {
    token_id = KeystoreToToken(keystore);
    if (!token_id) {
      std::move(callback).Run(mojom::KeystoreBinaryResult::NewError(
          mojom::KeystoreError::kUnsupportedKeystoreType));
      return;
    }
  }

  chromeos::platform_keys::KeyType key_type;
  chromeos::platform_keys::HashAlgorithm hash_algorithm;
  if (!UnpackSigningScheme(scheme, &key_type, &hash_algorithm)) {
    std::move(callback).Run(mojom::KeystoreBinaryResult::NewError(
        mojom::KeystoreError::kUnsupportedAlgorithmType));
    return;
  }

  PlatformKeysService* service = GetPlatformKeys();
  std::string data_str(data.begin(), data.end());
  std::string public_key_str(public_key.begin(), public_key.end());
  auto cb = base::BindOnce(&KeystoreServiceAsh::DidSign, std::move(callback));

  switch (key_type) {
    case chromeos::platform_keys::KeyType::kRsassaPkcs1V15:
      if (hash_algorithm == chromeos::platform_keys::HASH_ALGORITHM_NONE) {
        service->SignRSAPKCS1Raw(token_id, data_str, public_key_str,
                                 std::move(cb));
        return;
      }
      service->SignRSAPKCS1Digest(token_id, data_str, public_key_str,
                                  hash_algorithm, std::move(cb));
      return;
    case chromeos::platform_keys::KeyType::kEcdsa:
      service->SignECDSADigest(token_id, data_str, public_key_str,
                               hash_algorithm, std::move(cb));
      return;
  }
}

// static
void KeystoreServiceAsh::DidSign(SignCallback callback,
                                 const std::string& signature,
                                 chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::move(callback).Run(mojom::KeystoreBinaryResult::NewBlob(
        std::vector<uint8_t>(signature.begin(), signature.end())));
  } else {
    std::move(callback).Run(mojom::KeystoreBinaryResult::NewError(
        chromeos::platform_keys::StatusToKeystoreError(status)));
  }
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::GetKeyTags(const std::vector<uint8_t>& public_key,
                                    GetKeyTagsCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  GetKeyPermissions()->IsCorporateKey(
      public_key,
      base::BindOnce(&KeystoreServiceAsh::DidGetKeyTags, std::move(callback)));
}

// static
void KeystoreServiceAsh::DidGetKeyTags(GetKeyTagsCallback callback,
                                       absl::optional<bool> corporate,
                                       chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  using KeyTag = crosapi::mojom::KeyTag;

  crosapi::mojom::GetKeyTagsResultPtr result_ptr;

  if (status == chromeos::platform_keys::Status::kSuccess) {
    DCHECK(corporate.has_value());
    static_assert(sizeof(uint64_t) >= sizeof(KeyTag),
                  "Too many enum values for uint64_t");

    uint64_t tags = static_cast<uint64_t>(KeyTag::kNoTags);
    if (corporate.value()) {
      tags |= static_cast<uint64_t>(KeyTag::kCorporate);
    }
    result_ptr = crosapi::mojom::GetKeyTagsResult::NewTags(tags);
  } else {
    result_ptr = crosapi::mojom::GetKeyTagsResult::NewError(
        chromeos::platform_keys::StatusToKeystoreError(status));
  }

  std::move(callback).Run(std::move(result_ptr));
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::AddKeyTags(const std::vector<uint8_t>& public_key,
                                    uint64_t tags,
                                    AddKeyTagsCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  if (tags == static_cast<uint64_t>(mojom::KeyTag::kNoTags)) {
    std::move(callback).Run(/*is_error=*/false,
                            crosapi::mojom::KeystoreError::kUnknown);
    return;
  }

  if (tags == static_cast<uint64_t>(mojom::KeyTag::kCorporate)) {
    GetKeyPermissions()->SetCorporateKey(
        public_key, base::BindOnce(&KeystoreServiceAsh::DidAddKeyTags,
                                   std::move(callback)));
    return;
  }

  std::move(callback).Run(/*is_error=*/true,
                          crosapi::mojom::KeystoreError::kUnsupportedKeyTag);
}

// static
void KeystoreServiceAsh::DidAddKeyTags(AddKeyTagsCallback callback,
                                       chromeos::platform_keys::Status status) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (status == chromeos::platform_keys::Status::kSuccess) {
    std::move(callback).Run(/*is_error=*/false, mojom::KeystoreError::kUnknown);
  } else {
    std::move(callback).Run(
        /*is_error=*/true,
        chromeos::platform_keys::StatusToKeystoreError(status));
  }
}

//------------------------------------------------------------------------------

void KeystoreServiceAsh::CanUserGrantPermissionForKey(
    const std::vector<uint8_t>& public_key,
    CanUserGrantPermissionForKeyCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  // Strictly speaking, crosapi::CanUserGrantPermissionForKeyCallback is a
  // different type than platform_keys::CanUserGrantPermissionForKeyCallback.
  // But as long as signatures are the same, we can just pass `callback`.
  GetKeyPermissions()->CanUserGrantPermissionForKey(public_key,
                                                    std::move(callback));
}

}  // namespace crosapi


text:
<?php

namespace Drupal\mailchimp_lists\Plugin\Field\FieldType;

use Drupal\Component\Utility\Html;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;

/**
 * Plugin implementation of the 'mailchimp_lists_subscription' field type.
 *
 * @FieldType (
 *   id = "mailchimp_lists_subscription",
 *   label = @Translation("Mailchimp Subscription"),
 *   description = @Translation("Allows an entity to be subscribed to a Mailchimp list."),
 *   default_widget = "mailchimp_lists_select",
 *   default_formatter = "mailchimp_lists_subscribe_default"
 * )
 */
class MailchimpListsSubscription extends FieldItemBase {

  /**
   * {@inheritdoc}
   */
  public static function defaultStorageSettings() {
    return array(
      'mc_list_id' => '',
      'double_opt_in' => 0,
      'send_welcome' => 0,
    ) + parent::defaultStorageSettings();
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultFieldSettings() {
    return array(
      'show_interest_groups' => 0,
      'interest_groups_label' => '',
      'merge_fields' => array(),
      'unsubscribe_on_delete' => 0,
    ) + parent::defaultFieldSettings();
  }

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    $columns = array(
      'subscribe' => array(
        'type' => 'int',
        'size' => 'tiny',
        'not null' => TRUE,
        'default' => 0,
      ),
      'interest_groups' => array(
        'type' => 'text',
        'size' => 'normal',
        'not null' => TRUE,
      ),
    );
    return array(
      'columns' => $columns,
    );
  }

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['subscribe'] = DataDefinition::create('boolean')
      ->setLabel(t('Subscribe'))
      ->setDescription(t('True when an entity is subscribed to a list.'));

    $properties['interest_groups'] = DataDefinition::create('string')
      ->setLabel(t('Interest groups'))
      ->setDescription(t('Interest groups selected for a list.'));

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
    $element = parent::storageSettingsForm($form, $form_state, $has_data);

    $lists = mailchimp_get_lists();
    $options = array('' => t('-- Select --'));
    foreach ($lists as $mc_list) {
      $options[$mc_list->id] = $mc_list->name;
    }

    $field_map = \Drupal::entityManager()->getFieldMap();

    $field_definitions = array();
    foreach ($field_map as $entity_type => $fields) {
      $field_definitions[$entity_type] = \Drupal::entityManager()->getFieldStorageDefinitions($entity_type);
    }

    // Prevent MailChimp lists that have already been assigned to a field
    // appearing as field options.
    foreach ($field_map as $entity_type => $fields) {
      foreach ($fields as $field_name => $field_properties) {
        if ($field_properties['type'] == 'mailchimp_lists_subscription') {
          /* @var $field \Drupal\field\Entity\FieldStorageConfig */
          $field = $field_definitions[$entity_type][$field_name];
          $field_settings = $field->getSettings();

          if (($field_name != $this->getFieldDefinition()->getName()) && isset($field_settings['mc_list_id'])) {
            unset($options[$field_settings['mc_list_id']]);
          }
        }
      }
    }

    $refresh_lists_url = Url::fromRoute('mailchimp_lists.refresh');
    $mailchimp_url = Url::fromUri('https://admin.mailchimp.com', array('attributes' => array('target' => '_blank')));

    $element['mc_list_id'] = array(
      '#type' => 'select',
      '#title' => t('MailChimp List'),
      '#multiple' => FALSE,
      '#description' => t('Available MailChimp lists which are not already
        attached to Mailchimp Subscription Fields. If there are no options,
        make sure you have created a list at @MailChimp first, then @cacheclear.',
        array(
          '@MailChimp' => Link::fromTextAndUrl('MailChimp', $mailchimp_url)->toString(),
          '@cacheclear' => Link::fromTextAndUrl('clear your list cache', $refresh_lists_url)->toString(),
        )),
      '#options' => $options,
      '#default_value' => $this->getSetting('mc_list_id'),
      '#required' => TRUE,
      '#disabled' => $has_data,
    );
    $element['double_opt_in'] = array(
      '#type' => 'checkbox',
      '#title' => 'Require subscribers to Double Opt-in',
      '#description' => 'New subscribers will be sent a link with an email they must follow to confirm their subscription.',
      '#default_value' => $this->getSetting('double_opt_in'),
      '#disabled' => $has_data,
    );

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
    $element = parent::fieldSettingsForm($form, $form_state);
    $mc_list_id = $this->getFieldDefinition()->getSetting('mc_list_id');

    if (empty($mc_list_id)) {
      drupal_set_message(t('Select a list to sync with on the Field Settings tab before configuring the field instance.'), 'error');
      return $element;
    }
    $this->definition;
    $instance_settings = $this->definition->getSettings();

    $element['show_interest_groups'] = array(
      '#title' => "Enable Interest Groups",
      '#type' => "checkbox",
      '#default_value' => $instance_settings['show_interest_groups'],
    );
    $element['interest_groups_label'] = array(
      '#title' => "Interest Groups Label",
      '#type' => "textfield",
      '#default_value' => !empty($instance_settings['interest_groups_label']) ? $instance_settings['interest_groups_label'] : 'Interest Groups',
    );
    $element['merge_fields'] = array(
      '#type' => 'fieldset',
      '#title' => t('Merge Fields'),
      '#description' => t('Multi-value fields will only sync their first value to Mailchimp, as Mailchimp does not support multi-value fields.'),
      '#tree' => TRUE,
    );

    $element['unsubscribe_on_delete'] = array(
      '#title' => "Unsubscribe on deletion",
      '#type' => "checkbox",
      '#description' => t('Unsubscribe entities from this list when they are deleted.'),
      '#default_value' => $instance_settings['unsubscribe_on_delete'],
    );

    $mv_defaults = $instance_settings['merge_fields'];
    $mergevars = mailchimp_get_mergevars(array($mc_list_id));

    $field_config = $this->getFieldDefinition();

    $fields = $this->getFieldmapOptions($field_config->get('entity_type'), $field_config->get('bundle'));
    $required_fields = $this->getFieldmapOptions($field_config->get('entity_type'), $field_config->get('bundle'), TRUE);

    // Prevent this subscription field appearing as a merge field option.
    $field_name = $this->getFieldDefinition()->getName();
    unset($fields[$field_name]);

    $fields_flat = OptGroup::flattenOptions($fields);

    foreach ($mergevars[$mc_list_id] as $mergevar) {
      $default_value = isset($mv_defaults[$mergevar->tag]) ? $mv_defaults[$mergevar->tag] : -1;
      $element['merge_fields'][$mergevar->tag] = array(
        '#type' => 'select',
        '#title' => Html::escape($mergevar->name),
        '#default_value' => array_key_exists($default_value, $fields_flat) ? $default_value : '',
        '#required' => $mergevar->required,
      );
      if (!$mergevar->required || $mergevar->tag === 'EMAIL') {
        $element['merge_fields'][$mergevar->tag]['#options'] = $fields;
        if ($mergevar->tag === 'EMAIL') {
          $element['merge_fields'][$mergevar->tag]['#description'] = t('Any entity with an empty or invalid email address field value will simply be ignored by the Mailchimp subscription system. This is why the Email field is the only required merge field which can sync to non-required fields.');
        }
      }
      else {
        $element['merge_fields'][$mergevar->tag]['#options'] = $required_fields;
        $element['merge_fields'][$mergevar->tag]['#description'] = t("Only 'required' and 'calculated' fields are allowed to be synced with Mailchimp 'required' merge fields.");
      }
    }
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function isEmpty() {
    $value = $this->getValue();
    return (($value === NULL) || ($value === ''));
  }

  /**
   * {@inheritdoc}
   */
  public function preSave() {
    parent::preSave();

    $choices = $this->value;

    // Only act if the field has a value to prevent unintentional unsubscription.
    if (!empty($choices)) {
      mailchimp_lists_process_subscribe_form_choices($choices, $this, $this->getEntity());
    }
  }

  /**
   * Returns the field 'subscribe' value.
   *
   * @return bool
   */
  public function getSubscribe() {
    if (isset($this->values['value'])) {
      return ($this->values['value']['subscribe'] == 1);
    }

    return NULL;
  }

  /**
   * Returns the field 'interest_groups' value.
   *
   * @return array
   */
  public function getInterestGroups() {
    if (isset($this->values['value'])) {
      return $this->values['value']['interest_groups'];
    }

    return NULL;
  }

  /**
   * Get an array with all possible Drupal properties for a given entity type.
   *
   * @param string $entity_type
   *   Name of entity whose properties to list.
   * @param string $entity_bundle
   *   Optional bundle to limit available properties.
   * @param bool $required
   *   Set to TRUE if properties are required.
   * @param string $prefix
   *   Optional prefix for option IDs in the options list.
   * @param string $tree
   *   Optional name of the parent element if this options list is part of a tree.
   *
   * @return array
   *   List of properties that can be used as an #options list.
   */
  private function getFieldmapOptions($entity_type, $entity_bundle = NULL, $required = FALSE, $prefix = NULL, $tree = NULL) {
    $options = array();
    if (!$prefix) {
      $options[''] = t('-- Select --');
    }

    $properties = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_bundle);

    /*
      if (isset($entity_bundle)) {
        $info = entity_get_property_info($entity_type);
        $properties = $info['properties'];
        if (isset($info['bundles'][$entity_bundle])) {
          $properties += $info['bundles'][$entity_bundle]['properties'];
        }
      }
    */

    foreach ($properties as $key => $property) {
      $keypath = $prefix ? $prefix . ':' . $key : $key;
      $type = isset($property->type) ? entity_property_extract_innermost_type($property->type) : 'text';
      $is_entity = ($type == 'entity');// || (bool) entity_get_info($type);

      $label = $property->getLabel();
      $bundle = $property->getTargetBundle();

      if ($is_entity) {
        // We offer fields on related entities (useful for field collections).
        // But we only offer 1 level of depth to avoid loops.
        if (!$prefix) {
          $options[$label] = $this->getFieldmapOptions($type, $bundle, $required, $keypath, $label);
        }
      }
      elseif (!$required || $property->isRequired() || $property->isComputed()) {
        //if (isset($property['field']) && $property['field'] && !empty($property['property info'])) {
        //  foreach ($property['property info'] as $sub_key => $sub_prop) {
        //    $label = isset($tree) ? $tree . ' - ' . $property['label'] : $property['label'];
        //    $options[$label][$keypath . ':' . $sub_key] = $sub_prop['label'];
        //  }
        //}
        //else {
        $options[$keypath] = $label;
        //}
      }
    }
    return $options;
  }
}


text:
title: Audiovisuelles Medienprojekt 1
modulverantwortlich: hk
modulniveau: bachelor
kuerzel: AVM1
untertitel: null
studiensemester: 3
kategorie: null
sprache: deutsch
zuordnung-zum-curriculum: Medieninformatik Bachelor
kreditpunkte: 5
voraussetzungenNachPruefungsordnung: keine
empfohleneVoraussetzungen: Einführung in die Medieninformatik
published: true
veranstaltungsform: Vor Ort und Remote
typ: pm
layout: modulbeschreibung
infourl:
studienleistungen:
  Teamleistung:
    art: Projektarbeit
    erstpruefer: hk
    zweitpruefer: gh
---

## Kurzbeschreibung
Im Mittelpunkt dieses Moduls steht die digitale audiovisuelle Medienproduktion in den Formaten Porträt- und Dokumentarfilm.

## Lehrform/SWS
4 SWS: Vorlesung 2 SWS; Projekt 2 SWS

## Arbeitsaufwand
Gesamtaufwand 150h, davon
- 36h Vorlesung
- 36h Projektarbeit
- 78h Selbststudium

## Angestrebte Lernergebnisse
- Die Studierenden kennen die grundlegenden Erzählformen und Formate audiovisueller Medien und können auf der Basis klassischer Erzählmuster eigene audiovisuelle Erzählformen entwickeln.

- Die Studierenden können die in der audiovisuellen Produktion auftretende Problemstellungen selbstständig lösen und die verwendeten Medientechnologien, wie Videokamera, Tonaufnahmegeräte und Schnittsysteme technisch richtig und gestalterisch aussagekräftig einzusetzen.

- Sie sind befähigt zur Analyse, Diskussion und zur kritischen Betrachtung audiovisueller Medieninhalte.

- Die Studierenden können in den unterschiedlichsten Berufsfeldern digitaler audiovisueller Medien die Entwicklung und den Einsatz audiovisuellen Content beraten, planen, selbst durchführen und verantworten.

## Inhalt
Im Mittelpunkt dieses Moduls steht die digitale audiovisuelle Medienproduktion.

Die praktische Umsetzung des Vorlesungsstoffes, die Kommunikation und Zusammenarbeit im Team  und die Präsentation von eigenen Ideen und Projekten sind die Lerninhalte des Moduls "Audiovisuelles Medienprojekt". Neben der Fachkompetenz und Methodenkompetenz stehen in diesem Modul gerade die sogenannten Softskills Teamfähigkeit und Kommunikationsfähigkeit im Focus.

Die Projektarbeit gliedert sich dabei in die selbstständige Entwicklung, Ausarbeitung und Präsentation eines Filmthemas, in die praktische Umsetzung in einem Filmprojekt und in die Nachbearbeitung und Montage in einer dramaturgischen Erzählform.

Begleitend zu der Produktion werden folgende fachspezifischen Inhalte thematisiert und in Übungsaufgaben vertieft:

- Video- und Audioaufnahmetechnik
- Filmsprache
- Lichtsetzung
- Tonaufnahme
- Dokumentarfilm und Interview
- Dramaturgie
- Inszenierung
- Schnitt und Montage

## Medienformen
- Vorlesungen mit Folienpräsentationen
- Workshops zu Anwendungsprogrammen
- Beispiele aus verschiedenen Medien: Filmbeispiele, Webvideos
- Audiovisuelle Aufnahme- und Wiedergabegeräte
- Lehrfilme und Video-Tutorials

## Literatur
- James Monaco, Film verstehen, Rowolth Taschenbuch Verlag Hamburg, 1980, ISBN 3-499-162717
- Syd Field, Drehbuchschreiben für Film und Fernsehen, München 2003, ISBN 354836473X
- Steven D. Katz, Die Richtige Einstellung, Zweitausendeins, Frankfurt a.M.1998,ISBN 3-86150-229-1
- David Lewis Yewdall, Practical Art of Motion Picture Sound, Focal Press, USA 2003, ISBN 0-240-80525-9
- Hans Kornacher & Manfred Stross, Dokumentarisches Videofilmen, Augustus Verlag, Augsburg, 1992, ISBN 3-8043-5474-2
- Hans Beller Hg., Handbuch der Filmmontage, München: TR-Verlagsunion, 1993, ISBN 3-8058-2357-6
- Karel Reisz, Gavin Millar, Geschichte und Technik der Filmmontage, München: Filmlandpresse, 1988, ISBN 3-88690-071-1
- Chris Vogler, Die Reise des Drehbuchschreibens, Verlag Zweitausendeins


text:
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.camunda.bpm.engine.rest.history;

import static com.jayway.restassured.RestAssured.expect;
import static com.jayway.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.core.Response.Status;

import org.camunda.bpm.engine.AuthorizationException;
import org.camunda.bpm.engine.history.CleanableHistoricProcessInstanceReport;
import org.camunda.bpm.engine.history.CleanableHistoricProcessInstanceReportResult;
import org.camunda.bpm.engine.rest.AbstractRestServiceTest;
import org.camunda.bpm.engine.rest.util.container.TestContainerRule;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;

import com.jayway.restassured.http.ContentType;

public class CleanableHistoricProcessInstanceReportServiceTest extends AbstractRestServiceTest {

  @ClassRule
  public static TestContainerRule rule = new TestContainerRule();

  protected static final String HISTORY_URL = TEST_RESOURCE_ROOT_PATH + "/history/process-definition";
  protected static final String HISTORIC_REPORT_URL = HISTORY_URL + "/cleanable-process-instance-report";
  protected static final String HISTORIC_REPORT_COUNT_URL = HISTORIC_REPORT_URL + "/count";

  private CleanableHistoricProcessInstanceReport historicProcessInstanceReport;

  @Before
  public void setUpRuntimeData() {
    setupHistoryReportMock();
  }

  private void setupHistoryReportMock() {
    CleanableHistoricProcessInstanceReport report = mock(CleanableHistoricProcessInstanceReport.class);

    when(report.processDefinitionIdIn(anyString())).thenReturn(report);
    when(report.processDefinitionKeyIn(anyString())).thenReturn(report);

    CleanableHistoricProcessInstanceReportResult reportResult = mock(CleanableHistoricProcessInstanceReportResult.class);

    when(reportResult.getProcessDefinitionId()).thenReturn(EXAMPLE_PROCESS_DEFINITION_ID);
    when(reportResult.getProcessDefinitionKey()).thenReturn("aKey");
    when(reportResult.getProcessDefinitionName()).thenReturn("aName");
    when(reportResult.getProcessDefinitionVersion()).thenReturn(42);
    when(reportResult.getHistoryTimeToLive()).thenReturn(5);
    when(reportResult.getFinishedProcessInstanceCount()).thenReturn(10l);
    when(reportResult.getCleanableProcessInstanceCount()).thenReturn(5l);

    CleanableHistoricProcessInstanceReportResult anotherReportResult = mock(CleanableHistoricProcessInstanceReportResult.class);

    when(anotherReportResult.getProcessDefinitionId()).thenReturn("pdId");
    when(anotherReportResult.getProcessDefinitionKey()).thenReturn("pdKey");
    when(anotherReportResult.getProcessDefinitionName()).thenReturn("pdName");
    when(anotherReportResult.getProcessDefinitionVersion()).thenReturn(33);
    when(anotherReportResult.getHistoryTimeToLive()).thenReturn(null);
    when(anotherReportResult.getFinishedProcessInstanceCount()).thenReturn(13l);
    when(anotherReportResult.getCleanableProcessInstanceCount()).thenReturn(0l);

    List<CleanableHistoricProcessInstanceReportResult> mocks = new ArrayList<CleanableHistoricProcessInstanceReportResult>();
    mocks.add(reportResult);
    mocks.add(anotherReportResult);

    when(report.list()).thenReturn(mocks);
    when(report.count()).thenReturn((long) mocks.size());

    historicProcessInstanceReport = report;
    when(processEngine.getHistoryService().createCleanableHistoricProcessInstanceReport()).thenReturn(historicProcessInstanceReport);
  }

  @Test
  public void testGetReport() {
    given()
    .then().expect()
      .statusCode(Status.OK.getStatusCode())
      .contentType(ContentType.JSON)
    .when().get(HISTORIC_REPORT_URL);

    InOrder inOrder = Mockito.inOrder(historicProcessInstanceReport);
    inOrder.verify(historicProcessInstanceReport).list();
  }

  @Test
  public void testMissingAuthorization() {
    String message = "not authorized";
    when(historicProcessInstanceReport.list()).thenThrow(new AuthorizationException(message));

    given()
    .then().expect()
      .statusCode(Status.FORBIDDEN.getStatusCode())
      .contentType(ContentType.JSON)
      .body("type", equalTo(AuthorizationException.class.getSimpleName()))
      .body("message", equalTo(message))
    .when().get(HISTORIC_REPORT_URL);
  }

  @Test
  public void testListParameters() {
    String aProcDefId = "anProcDefId";
    String anotherProcDefId = "anotherProcDefId";

    String aProcDefKey = "anProcDefKey";
    String anotherProcDefKey = "anotherProcDefKey";

    given()
      .queryParam("processDefinitionIdIn", aProcDefId + "," + anotherProcDefId)
      .queryParam("processDefinitionKeyIn", aProcDefKey + "," + anotherProcDefKey)
    .then()
      .expect()
        .statusCode(Status.OK.getStatusCode())
        .contentType(ContentType.JSON)
      .when()
        .get(HISTORIC_REPORT_URL);

    verify(historicProcessInstanceReport).processDefinitionIdIn(aProcDefId, anotherProcDefId);
    verify(historicProcessInstanceReport).processDefinitionKeyIn(aProcDefKey, anotherProcDefKey);
    verify(historicProcessInstanceReport).list();
  }

  @Test
  public void testQueryCount() {
    expect()
      .statusCode(Status.OK.getStatusCode())
      .body("count", equalTo(2))
    .when()
      .get(HISTORIC_REPORT_COUNT_URL);

    verify(historicProcessInstanceReport).count();
  }

}